Curso Fudeba de ASM

 


Aula 2: Algumas técnicas iniciais.

 

   Antes de continuar com o curso, vamos a uma breve revisão do que foi visto na aula passada. A idéia era escrever um programa, ou seja, uma seqüência de ordens para "o mais loiro dos seres" (o seu computador) que escrevesse na tela a frase "Ola Mundo".

Bem, para fazer isso, estamos usando o NotePad como editor (tem no seu Windows) e o Microsoft Macro Assembler (M80/L80 que pode ser obtido aqui:

http://www.crosswinds.net/~adrianpage/m80l80pc.zip

   Vimos algumas coisas sobre o nosso ambiente: temos 64K posições de memória, que o processador (Z80) é trabalhador, mas de memória muito curta e temos de colocar tudo que ele precisa em seus registradores (colas) antes de mandarmos ele fazer alguma coisa, e que usamos para isso o comando LoaD (LD). Vimos também como fazemos para informar o Z80 para executar uma função do DOS (STROUT), indicando para ele o numero da função (LD C,STROUT) e mandando ele ir procurar o que essa maledeta função significa no endereço BDOS (que seria o endereço do "Altavista" do MSX-DOS), pedindo para que ele voltasse e continuasse com o nosso programa quando tivesse descoberto (e executado) o que a função STROUT faz, usando o comando CALL (CALL BDOS). Finalmente vimos como voltar ao prompt do MSX-DOS, usando o comando JumP para o endereço 0 da RAM (JP 0).
   Alem disso, vimos como escrever tudo isso de forma que o M80/L80 entendam o que estamos dizendo, como colocar dados na memória (APELIDO: DB 'dado') e como dar apelidos a endereços de memória (APELIDO EQU endereço). Vimos também que o Z80 é tão "loiro" que se você mandar ele executar um dado (JP FRASE ou CALL FRASE) ele realmente vai tentar (e vai fazer caca) e por isso temos de evitar que ele chegue até estes dados (ele é como um lemingue: se você não disser para ele para onde ele tem que ir, ele vai sempre em frente, se matando no buraco, ou seja, travando o seu micro, quando for o caso... mas não sem antes jogar um monte de lixo na tela e outras coisas mais bizarras).
   Finalmente vimos como "assemblar" o programa com o M80/L80, ou seja, como tornar o que escrevemos algo legível para o computador.

   Seria interessante se o leitor pudesse adquirir o livro "MSX TopSecret" de Edison Antônio Pires de Moraes (ele andou oferecendo copias pela MSXBr-L) pois este livro contem muitas especificações que serão úteis (pra não dizer vitais) mais pra frente no curso. Programar assembly "não existe" sem informações técnicas sobre a maquina em que estamos trabalhando.
   Caso o leitor saiba inglês, pode adquirir gratuitamente o MSX2 Technical Handbook, digitado por Nestor Soriano, que contem grande parte das informações que existem no MSX TopSecret (mas nem metade delas), mas já é algum começo. Este você pode obter na pagina do KonamiMan:

http://www.geocities.com/SiliconValley/Bay/9797/msx2.htm#MSX2TH

   Ou pegue direto:

http://www.geocities.com/SiliconValley/Bay/9797/th.bin

(renomeie para th.lzh e descomprima com o LHA)

   Nesta segunda aula veremos como estender o programa que já tínhamos para fazer alguma coisa a mais, como perguntar coisas ao usuário e interpretar as respostas... vamos lá!


   Conversando com o usuário...

   Bem, para "principiar" tomemos o programa final da aula anterior.

--- Cortar Aqui ---
STROUT  EQU     9
BDOS    EQU     5

START:
        LD      DE,FRASE                ; Indica posição do texto
        LD      C,STROUT                ; Indica função a ser executada pelo BDOS
        CALL    BDOS                    ; Manda o BDOS executar.
        JP      0                       ; Volta ao MSX-DOS

FRASE:          DB      'Alo, Mundo$'

        END
--- Cortar Aqui ---

   Antes de mais nada, salve este arquivo como PROG2.MAC, e então vamos fazer uma alteração substituindo o apelido FRASE pelo apelido NOMEDOPRG e substituir a frase "Alo, Mundo" por "Programa 2 - Conversando com o usuário":

--- Cortar Aqui ---
STROUT  EQU     9
BDOS    EQU     5

START:
        LD      DE,NOMEDOPRG            ; Indica texto do nome do programa
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.
        JP      0                       ; Volta ao MSX-DOS

NOMEDOPRG:      DB      'Programa 2 - Conversando com o usuario$'

        END
--- Cortar Aqui ---

   Isso acabou de se tornar o "nome" do nosso programa. Isso vai aparecer escrito sempre que nosso programa for executado. Aconselho, inclusive, que a cada "alteração" você "assemble" o programa usando o comando CL80 PROG2 no seu PC, e leve até o MSX para verificar o que acontece, ver se funciona, o que mudou, para você perceber efetivamente o que a alteração que você fez causou. Não vou ficar escrevendo a cada instante "assemble, leve para o MSX e veja o que deu" para não aumentar ainda mais o tamanho do curso, e também para não ficar "enchendo lingüiça" com informação já conhecida.
   Bem, vamos escrever mais alguma coisa na tela, adicionando uma segunda frase chamada AUTOR, de modo que você possa colocar que foi você quem fez o programa. Isso fica assim:

--- Cortar Aqui ---
STROUT  EQU     9
BDOS    EQU     5

START:
        LD      DE,NOMEDOPRG            ; Indica texto do nome do programa
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.
        JP      0                       ; Volta ao MSX-DOS

NOMEDOPRG:      DB      'Programa 2 - Conversando com o usuario$'
AUTOR:          DB      '  Por Daniel Caetano$'  ; coloque seu nome ao invés do meu.

        END
--- Cortar Aqui ---

   Bem, mas isso não vai fazer com que a frase apareça na tela. Precisamos mandar o Z80 mostra-la para nos. Onde mandar então? Bem, queremos que ela apareça depois dele ter mandado aparecer o nome do programa, mas antes do programa retornar ao prompt do MSX-DOS. Assim, a posição certa para inserir isso é entre o CALL BDOS e o JP 0. Nosso programa, com as alterações fica assim:

--- Cortar Aqui ---
STROUT  EQU     9
BDOS    EQU     5

START:
        LD      DE,NOMEDOPRG            ; Indica texto do nome do programa
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.
        LD      DE,AUTOR                ; Indica texto do nome do autor
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.
        JP      0                       ; Volta ao MSX-DOS

NOMEDOPRG:      DB      'Programa 2 - Conversando com o usuario$'
AUTOR:          DB      '  Por Daniel Caetano$'  ; coloque seu nome ao invés do meu.

        END
--- Cortar Aqui ---

   Se você observar bem, a coisa já começou a ficar meio bagunçada. Assim é bom inserir alguns comentários chamados "comentários de blocos", que são comentários que descrevem não o que um comando faz, mas o que um conjunto de comandos, logo abaixo deste comentário, fazem. Nos temos atualmente dois conjuntos básicos de comandos: um que escreve o nome do programa e outro que escreve o nome do autor. Vamos inserir esses comentários: face="Comic Sans MS, Arial, Helvetica, Sans-Serif" size="2">

--- Cortar Aqui ---
STROUT  EQU     9
BDOS    EQU     5

START:
        ; Mostra nome do programa
        LD      DE,NOMEDOPRG            ; Indica texto do nome do programa
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra nome do programador
        LD      DE,AUTOR                ; Indica texto do nome do autor
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        JP      0                       ; Volta ao MSX-DOS

NOMEDOPRG:      DB      'Programa 2 - Conversando com o usuario$'
AUTOR:          DB      '  Por Daniel Caetano$'  ; coloque seu nome ao invés do meu.

        END
--- Cortar Aqui ---

   Agora isso não parece tão interessante, mas no futuro você verá que isso é o que torna os programas "legíveis" depois de muito tempo que você não os vê. Se você executar esse programa, vai ver que algo "indesejado" aconteceu: o MSX-DOS *não* pulou linha entre uma frase e outra. Bem, pois é, você precisa dizer pra ele fazer isso. Pra isso você precisa dizer para ele retornar para a primeira posição da linha (CR, de Carriage Return) e pular uma linha (LF, de Line Feed). O CR é representado pelo numero 13, e o LF pelo numero 10. Assim, se você quiser pular linha, basta adicionar, no fim da frase, esses números, da seguinte forma:

NOMEDOPRG:      DB      'Programa 2 - Conversando com o usuario$',13,10

   MAS, se você fizer isso verá que não adiantou nada. O programa continua *não* pulando a linha. Uai... E por que não? Simples... Se você observar, tem um "$" antes do 13 e do 10, sendo que o $ diz para a função STROUT que o texto ACABOU. Então, meu amigo, esse 13 e esse 10 que você colocou ai' não estão nem sendo lidos. Você precisa coloca-los ANTES do $, o que pode ser feito assim:

NOMEDOPRG:      DB      'Programa 2 - Conversando com o usuario',13,10,'$'

   E agora sim ele vai pular as linhas direitinho. Nosso programa, já corrigido, fica assim:

--- Cortar Aqui ---
STROUT  EQU     9
BDOS    EQU     5

START:
        ; Mostra nome do programa
        LD      DE,NOMEDOPRG            ; Indica texto do nome do programa
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra nome do programador
        LD      DE,AUTOR                ; Indica texto do nome do autor
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        JP      0                       ; Volta ao MSX-DOS

NOMEDOPRG:      DB      'Programa 2 - Conversando com o usuario',13,10,'$'
AUTOR:          DB      '  Por Daniel Caetano',13,10,'$'

        END
--- Cortar Aqui ---

   Agora vamos pedir ao usuário que pressione uma tecla, receber a resposta e fazer alguma coisa com essa resposta.
   O primeiro passo é colocar a pergunta na tela, para que o usuário tome conhecimento de que precisa entrar com alguma informação. Isso pode ser feito da mesma forma com que fizemos antes com o nome do programa e nome do autor, inserindo a frase PERGU1:

--- Cortar Aqui ---
STROUT  EQU     9
BDOS    EQU     5

START:
        ; Mostra nome do programa
        LD      DE,NOMEDOPRG            ; Indica texto do nome do programa
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra nome do programador
        LD      DE,AUTOR                ; Indica texto do nome do autor
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra a pergunta
        LD      DE,PERGU1               ; Indica texto da pergunta
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        JP      0                       ; Volta ao MSX-DOS

NOMEDOPRG:      DB      'Programa 2 - Conversando com o usuarió,13,10,'$'
AUTOR:          DB      '  Por Daniel Caetano',13,10,'$'
PERGU1:         DB      '  Pressione alguma tecla: $'

        END
--- Cortar Aqui ---

   Legal, nosso programa, então, já mostra suas informações e faz uma pergunta. Bem, você deve ter notado que a saída do programa foi algo mais ou menos assim:

A>PROG2
Programa 2 - Conversando com o usuário
  Por Daniel Caetano
  Pressione alguma tecla:
A>

   Mas seria mais interessante se a saída fosse:

A>PROG2
Programa 2 - Conversando com o usuário
  Por Daniel Caetano

  Pressione alguma tecla:
A>

   Ficaria mais legal, não? Pois é. Isso é simples. Basta mandar pular mais uma linha, acrescentando um 10 adicional na frase:

AUTOR:          DB      '  Por Daniel Caetano',13,10,10,'$'

   Com isso alteramos o programa assim:

--- Cortar Aqui ---
STROUT  EQU     9
BDOS    EQU     5

START:
        ; Mostra nome do programa
        LD      DE,NOMEDOPRG            ; Indica texto do nome do programa
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra nome do programador
        LD      DE,AUTOR                ; Indica texto do nome do autor
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra a pergunta
        LD      DE,PERGU1               ; Indica texto da pergunta
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        JP      0                       ; Volta ao MSX-DOS

NOMEDOPRG:      DB      'Programa 2 - Conversando com o usuarió,13,10,'$'
AUTOR:          DB      '  Por Daniel Caetano',13,10,10,'$'
PERGU1:         DB      '  Pressione alguma tecla: $'

        END
--- Cortar Aqui ---

   Mas e agora? O programa já mostra tudo como a gente quer, mas como informar o Z80 para ele receber uma informação? Bem, existe uma função do MSX-DOS especifica para que possamos esperar que uma tecla seja pressionada. Essa função chama-se CONIN (CONsole IN, entrada de console, o que é o mesmo que entrada pelo teclado), e é a função de numero 1 do BDOS. Para chama-la, basta indicar a função, uma vez que nada mais é necessário para que o Z80 possa executar a operação. Isso pode ser feito assim:

        LD      C,1                     ; Indica a função que pega uma tecla
        CALL    5                       ; Chama BDOS

   Ou, usando os apelidos,

        CONIN   EQU     1
        BDOS    EQU     5
        LD      C,CONIN                 ; Indica a função que pega uma tecla
        CALL    BDOS                    ; Chama BDOS

   Bem, podemos inserir isso logo abaixo da pergunta, em nosso programa:

--- Cortar Aqui ---
BDOS    EQU     5

CONIN   EQU     1
STROUT  EQU     9

START:
        ; Mostra nome do programa
        LD      DE,NOMEDOPRG            ; Indica texto do nome do programa
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra nome do programador
        LD      DE,AUTOR                ; Indica texto do nome do autor
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra a pergunta
        LD      DE,PERGU1               ; Indica texto da pergunta
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Espera tecla ser pressionada
        LD      C,CONIN                 ; Indica a função que pega uma tecla
        CALL    BDOS                    ; Chama BDOS

        JP      0                       ; Volta ao MSX-DOS

NOMEDOPRG:      DB      'Programa 2 - Conversando com o usuarió,13,10,'$'
AUTOR:          DB      '  Por Daniel Caetano',13,10,10,'$'
PERGU1:         DB      '  Pressione alguma tecla: $'

        END
--- Cortar Aqui ---

   Bem, tudo jóia, mas quando executei isso tudo que aconteceu foi que o programa esperou eu pressionar uma tecla, mostrou a tecla que eu pressionei e voltou para o prompt. Note inclusive que o prompt apareceu exatamente na frente da pergunta... Se você observar no programa, isso ocorreu porque *não* mandamos pular linha nem voltar ao inicio da linha na frase PERGU1. Legal, mas como faço para saber, dentro do meu programa, qual foi a tecla que foi pressionada?
   O Z80 é um cara legal. Tem memória curta, mas é legal. Ele compartilha as colas dele com a gente. Existe uma cola especial que é normalmente onde ele coloca as respostas das coisas que perguntamos. Esse registrador (essa cola) é o chamado A. Por exemplo, usando a função CONIN do BDOS, perguntamos a ele que espere por uma tecla ser pressionada, e quando ela for, que nos informe qual foi. Assim, ele coloca o numero dessa tecla (aquele mesmo numero "ASCII" que eu citei na aula anterior) no registrador A. Então, já temos a resposta. A tecla digitada está prontinha para usarmos, no registrador A.
   Interessante, mas isso parece ainda não ajudar muito, não? Bem, antes de ver como podemos usar essa informação dada pelo Z80, vamos preparar uma resposta ao usuário. Vamos inserir a seguinte frase:

RESP1:          DB      13,10,10,'  A tecla pressionada foi: $'

   Note que antes da frase eu devidamente adicionei o retorno do cursor (13 = CR) e pulei duas linhas (10 = LF). Alem disso, não pulei linha nem retornei o cursor ao fim da frase, pois vou querer colocar a resposta (a tecla pressionada) DEPOIS dela, sem pular linha ou qualquer outra coisa. Adicionamos essa frase depois do ponto em que mandamos o Z80 ir pegar uma tecla pra gente:

--- Cortar Aqui ---
BDOS    EQU     5

CONIN   EQU     1
STROUT  EQU     9

START:
        ; Mostra nome do programa
        LD      DE,NOMEDOPRG            ; Indica texto do nome do programa
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra nome do programador
        LD      DE,AUTOR                ; Indica texto do nome do autor
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra a pergunta
        LD      DE,PERGU1               ; Indica texto da pergunta
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Espera tecla ser pressionada
        LD      C,CONIN                 ; Indica a função que pega uma tecla
        CALL    BDOS                    ; Chama BDOS

        ; Mostra a resposta
        LD      DE,RESP1                ; Indica texto da resposta
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.


        JP      0                       ; Volta ao MSX-DOS

NOMEDOPRG:      DB      'Programa 2 - Conversando com o usuarió,13,10,'$'
AUTOR:          DB      '  Por Daniel Caetano',13,10,10,'$'
PERGU1:         DB      '  Pressione alguma tecla: $'
RESP1:          DB      13,10,10,'  A tecla pressionada foi: $'

        END
--- Cortar Aqui ---

   Bom, o programa já quase faz o que a gente quer, mas ainda não faz. Eu tenho o valor da tecla, mas não sei como mostrar isso na tela. Ou sei? Sei sim. Existe no BDOS uma função que faz exatamente o contrario do que a CONIN faz. Não, essa função não escreve um caractere no teclado. Essa função escreve um caractere na tela, de acordo com o numero fornecido. Essa função chama-se CONOUT (CONsole OUT, ou saída para o console, ou saída para a tela - entenda, o console é um negocio que tem uma entrada (o teclado) e uma saída (a tela). Assim, sempre que você pedir uma ENTRADA (IN) do console, o computador lê o teclado. Sempre que você pedir uma SAIDA (OUT) para o console, o computador escreve na tela).

   Essa função é chamada normalmente, da mesma forma que a CONIN, pois é também uma função do MSX-DOS. A função CONOUT é a função de numero 2 do MSX-DOS. Há um porem... Ela requer que o numero do caractere a ser escrito esteja em uma outra cola... No registrador chamado E. Bem, temos o numero do caractere no registrador A, e precisamos que ele esteja no E. Existem diversas maneiras de copiar o valor de um registrador para outro, mas o mais fácil é usando o LoaD (LD). Se você fosse mandar alguém copiar a cola, você talvez dissesse:

   Copie na cola E o que estiver na cola A

   Para o Z80 é a mesma coisa:

         LD      E,A

   E pronto... após isso, o valor do caractere vai estar onde queremos, no registrador E. Note que o comando LoaD (LD) COPIA o valor de um registrador para outro, portanto, o que quer que estivesse no registrador E foi SUBSTITUIDO pelo valor do registrador A. Alem disso, como se trata de uma COPIA, o valor que estava no registrador A (o numero do caractere) CONTINUA lá.
   Inserindo essa nova informação no nosso programa, temos:

--- Cortar Aqui ---
BDOS    EQU     5

CONIN   EQU     1
STROUT  EQU     9

START:
        ; Mostra nome do programa
        LD      DE,NOMEDOPRG            ; Indica texto do nome do programa
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra nome do programador
        LD      DE,AUTOR                ; Indica texto do nome do autor
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra a pergunta
        LD      DE,PERGU1               ; Indica texto da pergunta
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Espera tecla ser pressionada e coloca seu valor em A
        LD      C,CONIN                 ; Indica a função que pega uma tecla
        CALL    BDOS                    ; Chama BDOS

        ; Mostra a resposta
        LD      DE,RESP1                ; Indica texto da resposta
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.
        LD      E,A                     ; Copia valor do caractere para E

        JP      0                       ; Volta ao MSX-DOS

NOMEDOPRG:      DB      'Programa 2 - Conversando com o usuarió,13,10,'$'
AUTOR:          DB      '  Por Daniel Caetano',13,10,10,'$'
PERGU1:         DB      '  Pressione alguma tecla: $'
RESP1:          DB      13,10,10,'  A tecla pressionada foi: $'

        END
--- Cortar Aqui ---

   Mas só isso não adianta, né? Precisamos, alem disso, usar nosso conhecimento da função CONOUT para mostrar o caractere. Para tanto vamos inserir mais isso no programa:

        CONOUT  EQU     2

        LD      C,CONOUT                ; Indica a função exibe um caractere na tela
        CALL    BDOS                    ; Chama BDOS

   Ficando nosso programa assim:

--- Cortar Aqui ---
BDOS    EQU     5

CONIN   EQU     1
CONOUT  EQU     2
STROUT  EQU     9

START:
        ; Mostra nome do programa
        LD      DE,NOMEDOPRG            ; Indica texto do nome do programa
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra nome do programador
        LD      DE,AUTOR                ; Indica texto do nome do autor
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra a pergunta
        LD      DE,PERGU1               ; Indica texto da pergunta
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Espera tecla ser pressionada e coloca seu valor em A
        LD      C,CONIN                 ; Indica a função que pega uma tecla
        CALL    BDOS                    ; Chama BDOS

        ; Mostra a resposta
        LD      DE,RESP1                ; Indica texto da resposta
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.
        LD      E,A                     ; Copia valor do caractere para E
        LD      C,CONOUT                ; Indica a função exibe um caractere na tela
        CALL    BDOS                    ; Chama BDOS

        JP      0                       ; Volta ao MSX-DOS

NOMEDOPRG:      DB      'Programa 2 - Conversando com o usuarió,13,10,'$'
AUTOR:          DB      '  Por Daniel Caetano',13,10,10,'$'
PERGU1:         DB      '  Pressione alguma tecla: $'
RESP1:          DB      13,10,10,'  A tecla pressionada foi: $'

        END
--- Cortar Aqui ---

   Beleza? Bem, se você perdeu tempo para executar esse programa, você vai ver que, sinistramente, algo deu errado. Esse programa sempre vai dizer que a tecla pressionada foi a tecla $. E porque isso acontece?
   Será que tem um bug...? COM CERTEZA, e um bug conceitual, daqueles mais difíceis de se encontrar. Como vamos encontra-lo? Bem, fizemos algumas coisas entre o momento em que obtivemos o valor do caractere em A e o momento em que copiamos ele para E. Vamos ver se, eliminando esse monte de coisa, a saída do programa acontece normalmente. Vamos "comentar" as linhas adicionando um ";" na frente delas, ficando nosso programa assim:

--- Cortar Aqui ---
BDOS    EQU     5

CONIN   EQU     1
CONOUT  EQU     2
STROUT  EQU     9

START:
        ; Mostra nome do programa
        LD      DE,NOMEDOPRG            ; Indica texto do nome do programa
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra nome do programador
        LD      DE,AUTOR                ; Indica texto do nome do autor
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra a pergunta
        LD      DE,PERGU1               ; Indica texto da pergunta
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Espera tecla ser pressionada e coloca seu valor em A
        LD      C,CONIN                 ; Indica a função que pega uma tecla
        CALL    BDOS                    ; Chama BDOS

        ; Mostra a resposta
;       LD      DE,RESP1                ; Indica texto da resposta
;       LD      C,STROUT                ; Indica função de mostrar texto do BDOS
;       CALL    BDOS                    ; Manda o BDOS executar.
        LD      E,A                     ; Copia valor do caractere para E
        LD      C,CONOUT                ; Indica a função exibe um caractere na tela
        CALL    BDOS                    ; Chama BDOS

        JP      0                       ; Volta ao MSX-DOS

NOMEDOPRG:      DB      'Programa 2 - Conversando com o usuarió,13,10,'$'
AUTOR:          DB      '  Por Daniel Caetano',13,10,10,'$'
PERGU1:         DB      '  Pressione alguma tecla: $'
RESP1:          DB      13,10,10,'  A tecla pressionada foi: $'

        END
--- Cortar Aqui ---

   Ou seja, eliminamos tudo que acontece entre o momento em que pegamos o numero do caractere e o momento em que mandamos ele ser mostrado na tela. Rodemos o programa e ... veja que funciona! Note que agora o caractere aparece repetido duas vezes na tela (uma vez é o "retorno" normal da função CONIN e a outra é a saída da função CONOUT). Então podemos presumir que estamos pegando e mostrando o caractere corretamente. O que pode ter dado errado então? Bem, se você observar, verá que entre pegarmos o valor da tecla pressionada e tentarmos imprimir ele na tela, imprimimos um texto: " A tecla pressionada foi: "... O problema deve estar ai'.
   Lembra-se de que na primeira aula eu falei que o Z80 tinha poucos papeis de cola e que portanto ele precisava reciclar esses papeis? Provavelmente é o que está acontecendo. Em algum momento, quando o MSX-DOS está imprimindo a sua frase, ele usa o registrador A para guardar o numero do caractere a ser impresso na tela. Um indicio de que isso é verdade é que, se você observar, o valor que era sempre impresso como sendo "a tecla que você digitou" era o "$", que é exatamente o ultimo caractere da frase RESP1!
   Bem, então como fazemos? Será que a gente não pode nunca fazer nada entre pegar um dado e usa-lo? Claro que podemos. Se não pudéssemos, seria impossível trabalhar em Assembly. O que precisamos é guardar erra valor na MEMORIA, em um lugar seguro, antes de fazermos qualquer outra coisa. Existem muitos jeitos de guardar este dado na memória. Um é dizendo exatamente onde queremos que este dado seja guardado. Outro é deixando que o Z80 escolha para a gente. Neste momento vamos escolher a primeira opção. Então, primeiro vamos definir um lugar para esse valor ser colocado. Um bom lugar para isso é depois das frases. E como guardamos lugar para esse caractere? Simples, definimos uma "frase" para ele, de um caractere só. Isso pode ser feito assim:

 VAR1:   DB      0

   Isso define uma posição de memória apelidada de VAR1 (VARiavel 1) com o valor 0 dentro. É neste lugar que vamos mandar o Z80 colocar o valor que a gente recebe. E como vamos fazer isso? Exatamente com o comando LoaD. Ele também serve para copiar coisas da MEMORIA para um REGISTRADOR, ou de um REGISTRADOR para a MEMORIA. Se queremos colocar o dado em VAR1 e este dado está no registrador A, nada mais natural do que um comando:

         LD      VAR1,A

   Mas isso não vai funcionar, como diria o DalPoz. E porque? Bem, lembre-se que VAR1 é apenas uma apelido para um NOME de posição de memória. O Z80 iria ler isso ai' como "De um jeito de o nome 0 ser igual a 2", por exemplo. É claro que teremos problemas. Temos que dar um jeito de dizer a ele que coloque o valor de A DENTRO da posição de memória chamada VAR1. Para isso usamos os parênteses:

        LD      (VAR1),A

   Isso diz ao Z80 "Copie DENTRO da posição de memória chamada VAR1 o valor de A". Ai' sim obteremos sucesso, pois ele vai entender direitinho o que queremos dizer... o que em nosso programa fica:

--- Cortar Aqui ---
BDOS    EQU     5

CONIN   EQU     1
CONOUT  EQU     2
STROUT  EQU     9

START:
        ; Mostra nome do programa
        LD      DE,NOMEDOPRG            ; Indica texto do nome do programa
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra nome do programador
        LD      DE,AUTOR                ; Indica texto do nome do autor
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra a pergunta
        LD      DE,PERGU1               ; Indica texto da pergunta
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Espera tecla ser pressionada e coloca seu valor em A
        LD      C,CONIN                 ; Indica a função que pega uma tecla
        CALL    BDOS                    ; Chama BDOS
        LD      (VAR1),A                ; Grava o valor da tecla na MEMORIA.

        ; Mostra a resposta
;       LD      DE,RESP1                ; Indica texto da resposta
;       LD      C,STROUT                ; Indica função de mostrar texto do BDOS
;       CALL    BDOS                    ; Manda o BDOS executar.
        LD      E,A                     ; Copia valor do caractere para E
        LD      C,CONOUT                ; Indica a função exibe um caractere na tela
        CALL    BDOS                    ; Chama BDOS

        JP      0                       ; Volta ao MSX-DOS

NOMEDOPRG:      DB      'Programa 2 - Conversando com o usuarió,13,10,'$'
AUTOR:          DB      '  Por Daniel Caetano',13,10,10,'$'
PERGU1:         DB      '  Pressione alguma tecla: $'
RESP1:          DB      13,10,10,'  A tecla pressionada foi: $'
VAR1:           DB      0

        END
--- Cortar Aqui ---

   Ótimo, o dado já está na memória. Agora temos que, antes de chamar a função que nos mostra o caractere na tela, colocar no registrador E esse valor que está na posição de memória VAR1. O jeito mais natural de fazer isso é:

        LD      E,(VAR1)

   Ou seja, "Copie em E o que estiver DENTRO da posição de memória chamada VAR1". Substituindo a antiga instrução LD E,A (que tinha exatamente a função disso que estamos fazendo) por essa nova instrução LD E,(VAR1) e retirando os ";" das linhas que mostram o texto da resposta, teremos:

--- Cortar Aqui ---
BDOS    EQU     5

CONIN   EQU     1
CONOUT  EQU     2
STROUT  EQU     9

START:
        ; Mostra nome do programa
        LD      DE,NOMEDOPRG            ; Indica texto do nome do programa
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra nome do programador
        LD      DE,AUTOR                ; Indica texto do nome do autor
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra a pergunta
        LD      DE,PERGU1               ; Indica texto da pergunta
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Espera tecla ser pressionada e coloca seu valor em A
        LD      C,CONIN                 ; Indica a função que pega uma tecla
        CALL    BDOS                    ; Chama BDOS
        LD      (VAR1),A                ; Grava o valor da tecla na MEMORIA.

        ; Mostra a resposta
        LD      DE,RESP1                ; Indica texto da resposta
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.
        LD      E,(VAR1)                ; Copia para E o conteúdo de VAR1.
        LD      C,CONOUT                ; Indica a função exibe um caractere na tela
        CALL    BDOS                    ; Chama BDOS

        JP      0                       ; Volta ao MSX-DOS

NOMEDOPRG:      DB      'Programa 2 - Conversando com o usuarió,13,10,'$'
AUTOR:          DB      '  Por Daniel Caetano',13,10,10,'$'
PERGU1:         DB      '  Pressione alguma tecla: $'
RESP1:          DB      13,10,10,'  A tecla pressionada foi: $'
VAR1:           DB      0

        END
--- Cortar Aqui ---

   Parece que agora vai, não? Bem, ao tentar compilar você seguramente vai receber a seguinte mensagem:

C:\ASMMSX>cl80 prog2
M80/L80 Z80 Compiler - IBM PC
Ported by A&L Software
ÿ
MSX.M-80  1.00  01-Apr-85  (c) 1981,1985 Microsoft
A 0028'   3A 00AÉ              LD      E,(VAR1)                ; Copia para E o
 conteúdo de VAR1.

1 Fatal error(s)

C:\ASMMSX>

   E o seu programa não terá nem sido assemblado. O que esse código de erro significa? Significa que na linha apresentada ai' houve um erro. E no caso O erro é que esse comando LD E,(VAR1) não existe para o Z80!
   Caramba, você me pergunta, mas é tão lógico! Como assim, não existe? Simples, não existindo. E essa é a parte mais chata do Assembly. Lembra que eu disse que existiam colas especificas para algumas coisas? Pois é. Em um outro momento eu disse que o Z80 gostava de colocar repostas no registrador A. Pois aqui é exatamente um desses casos. Você só pode ler o conteúdo de UM BYTE da MEMORIA, ou seja, o valor de UMA única posição de memória *se* coloca-lo dentro do registrador A.
   Você vai dizer... poxa, mas eu preciso dele dentro do registrador E, e não dentro do registrador A. Felizmente você ainda pode usar o LoaD para copiar o valor de A para E. Assim, o comando:

        LD      E,(VAR1)                ; Copia para E o conteúdo de VAR1.

   Deve ser substituído por dois outros comandos, que tem um resultado final equivalente:

        LD      A,(VAR1)                ; Copia para A o conteúdo de VAR1
        LD      E,A                     ; Copia para E o conteúdo de A

   E o nosso programa finalmente fica assim:

--- Cortar Aqui ---
BDOS    EQU     5

CONIN   EQU     1
CONOUT  EQU     2
STROUT  EQU     9

START:
        ; Mostra nome do programa
        LD      DE,NOMEDOPRG            ; Indica texto do nome do programa
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra nome do programador
        LD      DE,AUTOR                ; Indica texto do nome do autor
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Mostra a pergunta
        LD      DE,PERGU1               ; Indica texto da pergunta
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.

        ; Espera tecla ser pressionada e coloca seu valor em A
        LD      C,CONIN                 ; Indica a função que pega uma tecla
        CALL    BDOS                    ; Chama BDOS
        LD      (VAR1),A                ; Grava o valor da tecla na MEMORIA.

        ; Mostra a resposta
        LD      DE,RESP1                ; Indica texto da resposta
        LD      C,STROUT                ; Indica função de mostrar texto do BDOS
        CALL    BDOS                    ; Manda o BDOS executar.
        LD      A,(VAR1)                ; Copia para A o conteúdo de VAR1
        LD      E,A                     ; Copia para E o conteúdo de A
        LD      C,CONOUT                ; Indica a função exibe um caractere na tela
        CALL    BDOS                    ; Chama BDOS

        JP      0                       ; Volta ao MSX-DOS

NOMEDOPRG:      DB      'Programa 2 - Conversando com o usuarió,13,10,'$'
AUTOR:          DB      '  Por Daniel Caetano',13,10,10,'$'
PERGU1:         DB      '  Pressione alguma tecla: $'
RESP1:          DB      13,10,10,'  A tecla pressionada foi: $'
VAR1:           DB      0

        END
--- Cortar Aqui ---

   Finalmente nosso programa está pronto e faz o que queremos. Nesta aula aprendemos novos comandos, e vimos como algumas limitações do Assembly do Z80 nos "enchem a paciência", mas teremos de conviver com elas se quisermos programas nesta linguagem. Adianto que isso que parece uma irritação agora no futuro não lhes incomodará mais, passando a ser algo "natural".
   Também vimos algumas novas funções, como a de pegar um caractere do teclado e a de mostrar um caractere na tela.
   Na próxima aula veremos como "otimizar" este programa e também como usar o BrMSX para verificar o resultado da programação, para evitar termos de ficar copiando as coisas em disquete e levando até o MSX toda hora... Vocês verão que isso é de grande auxilio e acelera muito a velocidade do desenvolvimento.

   Espero que tenham gostado. Quaisquer duvidas, enviem para a lista, eu ou qualquer um dos programadores da lista responderão tão breve quanto possível.

Abraços,

Daniel Caetano.

PS: Sugestões e comentários são bem-vindos. (^=