terça-feira, maio 16, 2023

MSX: Resumo de Operação Mega Assembler

O Mega Assembler, talvez tenha sido o cartucho de depuração mais utilizado por usuários finais na época de ouro do MSX. Muito embora as funções dele como depurador estejam abaixo de ferramentas mais atuais (como o Compass ou o Super-X), e seu assemblador ser inferior a um assemblador completo (como o M80 e o Gen80) e mais inferior ainda quando falamos de assembladores cross, como o TiniASM, SjASM ou o Chaos, ainda assim é uma ótima ferramenta para iniciantes em linguagem de máquina, pois seus comandos são simples, possui alguns utilitários interessantes para o MSX-BASIC, a compilação ocorre em memória, e o programa Assembly pode ser editado como um programa em MSX-BASIC, incluindo comentários.

Como manual do produto é relativamente confuso para iniciantes, e suas funções são dispersas e são mostrados exemplos não muito úteis, o objetivo deste artigo e agrupar melhor os comandos/funções e incluir exemplos mais práticos. Como os artigos citados, este também ficará sempre como "Work in Progress", pois vou complementando ele conforme veja necessidade de mostrar um procedimento específico.

Introdução

Existiram inúmeras versões deste programa, algumas inclusive recebendo nomes diferentes, as pirata-houses da época lançavam como sendo um produto seu, incluindo aí a Cybertron e a Orionsoft. Porém a versão aqui apresentada é a que foi ligeiramente melhorada pelo Adriano Cunha (chamada de versão 1.0b). Tanto a versão em .ROM como uma versão .COM criada por ele estão disponíveis. Uma ferramenta legal para converter arquivos fonte salvos no Mega Assembler (que não são gravados em texto puro) em arquivos ASCII também está disponível na MSX Adrian Page (link direto para download aqui).

A versão da Cybertron 1.0 pode ser baixada na página da MSX-PRO, a versão da Orionsoft 1.1 também pode ser baixada por lá.

Uma cópia ligeiramente alterada do manual da Cybertron, pode ser encontrada no site do DataCassete. O manual em formato TXT 40 colunas/MSX consta na versão do Adriano Cunha.

O Mega Assembler é um cartucho (16 Kbytes) que pode ser dividido em três funções primárias: Editor (assembler/disassembler), Monitor e utilitários para o MSX-BASIC, caso o cartucho seja inserido num slot mais baixo do que outro cartucho ou interface, o Mega Assembler entra antes, permitindo depois chavear e debugar a interface ou cartucho sem executá-los. Se o Mega Assembler foi inserido após um cartucho ou interface, então a interface é ativada antes do Mega Assembler (no caso de um ROM de jogo por exemplo, o MA não fica disponível).

Após ligar o MSX e entrar no MSX-BASIC, podemos chamar o Mega Assembler de duas maneiras distintas:

CALL START

ou

CALL ASM

CALL START inicializa todos os parâmetros e variáveis do Mega Assembler, inclusive apagando algum fonte que por ventura pudesse estar em memória. Deve ser usado o CALL START logo após a inicialização do MSX (ou após entrar e sair do MSX-DOS, ou quando usamos algum comando de disco), e chamadas subsequentes, usamos o CALL ASM, que apenas entra no modo Monitor/Editor do Mega Assembler, sem iniciar as variáveis (e nesta versão corrigida pelo Adriano, o código fonte fica preservado, também, exceto quando da reinicialização e da entrada/saída do MSX-DOS).

Estando no Mega Assembler, podemos usar o comando

BA

para retornarmos ao MSX-BASIC.

Comandos Adicionais do MSX-BASIC

Embora o foco do artigo seja a função de assemblador do Mega Assembler, e em segundo plano a função de monitor (sempre recomendo usar o Super-X, porém para quem tem apenas um MSX 1.0, o Mega Assembler é uma das opções mais fáceis), destaco que o Mega Assembler extende o MSX-BASIC com alguns comandos que podem ser úteis, não irei detalhar cada um deles, apenas farei uma citação e um exemplo, com alguma nota adicional. Para utilizar estes comandos não é necessário usar o CALL START ou o CALL ASM antes, eles já estão disponíveis a partir do BOOT (a não serm em algum caso onde a configuração de SLOTS não permita a inicialização do cartucho/ROM do Mega Assembler)

Editor de Caracteres

O Mega Assembler possui um modo chamado a partir do MSX-BASIC, que permite alterar a memória no chamado "formato de sprite", ou "formato de caracter", onde uma matriz 8x8 e ampliada permitindo ao usuário a ativação/desativação dos bits individuais desta matriz, funcionando como um editor de caracteres. O programa é simplório, ele não encontra bancos de caracteres ou coisa do tipo em seu MSX. Para chamar o editor de caracteres, basta comandar:

CALL EDITOR <endereço>

onde <endereço> é opcional e indica em qual local o editor vai buscar os dados para a edição. Se não for informado, o CALL EDITOR assume &HC000 como padrão.

(OC) Estranhamente, o parâmetro <endereço> parece não ter qualquer utilidade, visto que o CALL EDITOR sempre chama o endereço &HC000, sempre com lixo ou com o que você já editou.

Vamos a um exemplo prático, como não podemos alterar os caracteres da ROM, vamos fazer um pequeno programa em BASIC (mais adiante veremos como fazer em Mega Assembler) que cópia a tabela de caracteres da ROM para o endereço &HC000. Os caracteres, inicialmente, são definidos no slot 0 (ROM) a partir do endereço &H1BBF.

Programa simples para cópia do banco de caracteres da ROM para a RAM:

10 SCREEN 0:KEY OFF:WIDTH 40
20 FOR C=0 TO 2047
30 A=PEEK(&H1BBF+C)
40 POKE &HC000+C,A
50 NEXT

Após rodar o programa podemos iniciar o editor com

CALL EDITOR

Onde teremos a tela de edição disponível.

Inicialmente estamos no modo de seleção de caractere, onde as seguintes teclas são usadas:

  • [CURSOR] - Movem o cursor sobre os caracteres.
  • [TAB] - Volta para o MSX-BASIC.
  • [CTRL]+[STOP] - Volta para o MSX-BASIC com a mensagem de Break.
  • [SHIFT]+[HOME] - Transfere a tabela de caracteres do micro para o endereço de edição (ou seja, faz basicamente o mesmo que o programa acima, porém a ideia é ilustrar como podemos transferir bancos entre a memória).
  • [RETURN] - Vai para o modo de edição.

Ao passarmos para o modo de edição, iremos trabalhar na matriz ampliada do caractere, permitindo alterar seus bits, com as seguintes teclas de ação:

  • [CURSOR] - Movem o cursor sobre os bits do caractere.
  • [SPACE] - Inverte o estado do ponto sob o cursor.
  • [I] - Inverte todo o caractere em edição, atente que o I deve ser digitado em maiúsculo.
  • [S] - Espera uma tecla de cursor, rotacionando o caractere na direção da tecla pressionada, o S deve ser maiúsculo também.
  • [SHIFT]+[HOME] - Apaga o caractere em edição e posiciona o cursor no ponto 0x0 da matriz.
  • [CTRL]+[STOP] - Volta diretamente ao MSX-BASIC.
  • [RETURN] - Volta ao modo de edição.

Para usarmos a tabela editada podemos nos valer de algumas técnicas (inclusive usando o comando de cópia da RAM para VRAM do próprio Mega Assembler).

Podemos copiar os 2048 bytes a partir de &HC000 para o ponto da VRAM onde se encontra a tabela de padrões do modo de tela corrente, por exemplo, após inverter os caracteres de 0 a 9 usando o editor

podemos executar o seguinte programa em MSX-BASIC para copiar toda a tabela de &HC000 para o endereço &H800 (BASE(2), tabela de definição de padrões da SCREEN 0, supondo que já estejamos na mesma).

20 FOR C=0 TO 2047
30 A=PEEK(&HC000+C)
40 VPOKE BASE(2)+C,A
50 NEXT C

Outra forma é alterar a variável de sistema que indica onde está a tabela de caracteres. No exemplo acima, após comandarmos SCREEN novamente, a tabela da ROM é copiada para a tabela da VRAM. No entando se alterarmos o valor padrão (&H1BBF, banco de caracteres da ROM) da variável &HF920 (de 2 bytes) para &HC000, e alterarmos a variável &HF91F (que indica em qual slot está o banco de caracteres), podemos ter um efeito mais definitivo, pois mesmo comandando SCREEN (inclusive saindo de um modo para outro) o banco a ser copiado é o que está em &HC000 e não mais o que está em &H1BBF. Para desfazer esta alteração e voltar a usar o banco padrão, basta mudar as variáveis para &H1BBF e 0 novamente. O programa abaixo faz isso.

10 POKE &HF920,&H00
30 POKE &HF921,&HC0
40 POKE &HF91F,s
50 SCREEN n

Onde s é o slot no qual a RAM está alocada e n e o modo de SCREEN que desejamos entrar no momento.

Para saber mais sobre como encontrar o slot no qual a RAM está presente, veja o artigo: Localizando a Paginação da Memória  e os capítulos 0 e 3 do livro Aprofundando-se no MSX.

Recuperando Programas Apagados com NEW

Este é um dos comandos mais simples do Mega Assembler, após digitar o comando

NEW

no MSX-BASIC, o programa e "apagado" da memória (na verdade, apenas alguns bytes são alterados para que o MSX-BASIC pense não existir mais programas. Comandando

RENEW

o Mega Assembler ajusta estes bytes e recupera o programa que havia sido apagado. A única resalva é que o programa deve ser disparado logo após o NEW, pois uma inserção ou alteração de linha já danifica a área de texto do BASIC.

Verificação de Programas Gravados em Fita K-7

O Mega Assembler possui um comando que permite, após a gravação em fita de um binário via BSAVE "CAS:", verificar se o mesmo foi gravado corretamente. O comando em questão é o BVERIFY. Imaginemos uma situação onde gravamos o conteúdo da memória de &HC000 até &HC0FF em fita com o seguinte comando:

BSAVE "CAS:PROG1",&HC000,&HC0FF

Após a gravação, rebobine a fita até o ponto inicial e comande

CALL BVERIFY

Se o programa estiver corretamente gravado, teremos um OK na tela, caso contrário teremos um Verify error.

Identificar o HEADER de um programa gravado em fita K-7

Podemos facilmente identificar o cabeçalho de um programa binário gravado em K-7 com o comando CALL HEADER. Basta posicionar a fita no início do programa e comandar:

CALL HEADER

Para termos o resultado impresso na tela.

Resultado da verificação de cabeçalho:

Copiar Dados da RAM para a VRAM e vice-versa

Os comandos CALL COPYRV e CALL COPYVR servem respectivamente para copiar dados entre a RAM e a VRAM ou entre a VRAM e a RAM. Tendo a seguinte sintaxe:

CALL COPYRV (<endereço inicial da RAM>,<endereço final da RAM>,<endereço inicial da VRAM>)

e

CALL COPYVR (<endereço inicial da VRAM>,<endereço final da VRAM>,<endereço inicial da RAM>)

Obviamente, os endereços devem respeitar os limites tanto da RAM quanto da VRAM, lembrando que apenas a RAM ativa no MSX-BASIC pode ser usada para cópia. Adicionalmente a cópia deve fazer sentido a nível de VRAM. Como exemplo, podemos alterar o primeiro programa usado no comando CALL EDITOR, que transferia o conteúdo de uma área da RAM para a área de definição de padrões da SCREEN 0 na VRAM usando PEEK e POKE. Podemos usar os comandos conforme exemplo abaixo:

20 CALL COPYRV(&HC000,(&HC000+2047),BASE(2))

Obviamente, além da simplificação do programa, o mesmo é executado com uma velocidade muito superior ao primeiro exemplo.

Cópia de Tela

O Mega Assembler possui um conjunto de comandos relacionados a cópia de telas, sendo o principal deles CALL DUMP. Este comando tira uma cópia da tela nas SCREENs de 0 a 3, sprites inclusive nas telas que os suportam.

CALL DUMP

Adicionalmente temos o comando CALL SETGREY que habilita ou não a cópia da tela em escala de cinza, usando um padrão para cada cor ou imprimindo apenas os pontos com código de cor acima de 8.

  • CALL SETGREY(0) - Com escala de cinza desativada.
  • CALL SETGREY(1) - Com escala de cinza ativada.

Também existe um comando que permite a cópia da tela automaticamente ao pressionar a tecla [ESC], sem necessidade de chamar o comando CALL DUMP, este comportamento é alterado pelo comando CALL SETKEY.

  • CALL SETKEY(0) - Desativa cópia automática.
  • CALL SETKEY(1) - Ativa cópia automática.

O comando de cópia automática é desativado quando entramos no modo monitor do Mega Assembler.

Modo Monitor

A partir de agora vamos estudar a função de Monitor do Mega Assembler, esta função é de grande valia para a depuração de programas, apesar do livro Linguagem de Máquina (MSX Typer) usar na parte de depuração o Super-X, tudo pode ser feito no Mega Assembler, e em programas maiores com comentários e etc. o Mega Assembler será usado (embora, no modo Assembler).

O modo monitor é o modo padrão quando executamos um CALL START ou CALL ASM, a partir do prompt, podemos informar uma série de comandos para a operação deste modo.

No modo monitor, os comandos esperam sempre números em hexadecimal (diferente do modo assembler, que por padrão aceita números decimais, assim como ocorre no Super-X).

Páginação de Memória

Praticamente todos os comandos do monitor são afetados pelas configurações de página do MSX. Para consulta e alterar o esquema corrente, usamos o comando PAGE do monitor. A configuração dos slots, assim que o micro é inicializado, é definida da seguinte forma: Página 0 (de 0000 a 3FFF) no slot 0 (ROM), Página 1 (de 4000 a 7FFF) no slot 0 (ROM), Página 2 (de 8000 a BFFF) no slot da RAM (depende de cada máquina), Página 3 (de C000 a FFFF) também no slot da RAM. O comando PAGE tem basicamente 3 formas de uso:

PAGE ?

mostra a disposição atual das páginas.

PAGE

sem argumentos, coloca todas as páginas no slot da RAM.

PAGE [[<slot>][.[<slot>[.[<slot>][,[<slot>]]]]]

Permite a alteração das páginas ativas, <slot> aceita um valor entre 0 e 3, cada parâmetro corresponde a uma página, sendo assim, podemos selecionar de qual slot cada página irá ser selecionada. Atente apenas para o quarto parâmetro, a página 3, apesar de aceitar o parâmetro de slot, nunca é alterada, ficando sempre no slot da RAM (por razões obvias). Por exemplo:

PAGE 0,0

Seleciona as páginas 0 e 1 de memória para o slot 0.

PAGE 0,1,1,2

Seleciona a página 0 para o slot 0, e as páginas 1, 2 para o slot 1, e a página 3 para o slot 2. Supondo que exista alguma interface ou cartucho no slot 1, e a RAM fique no slot 2.

Perceba a falta de efeito ao tentar mudar a página 3 para um slot que não continha a RAM, apesar do PAGE aceitar o parâmetro, quando comandamos PAGE ? percebemos que a página 3 continua no slot da RAM (3 neste micro em particular).

Exibição e Alteração de Memória

O primeiro comando utilizado como monitor propriamente dito é o comando DM, com cuja sintaxe é:

DM <endereço>[,<deslocamento>]

Este comando mostra 128 bytes na tela, tanto em hexadecimal, quanto em ASCII, permitindo sua alteração ou apenas consulta. O parâmetro <deslocamento> é usado para adicionar ou subtrair um valor de cada valor apresentado. Pode ser útil com programas que usavam deslocamento para tentar confundir os "piratas", de modo que as strings de um programa não fossem localizadas diretamente (este valor pode variar de -7H a 80). Após entrar no modo de exibição/alteração, as seguintes teclas estão disponíveis:

  • [CURSOR] - Move o cursor pela memória.
  • [SELECT] - Troca o modo de edição de hexadecimal para ASCII e vice-versa.
  • [RETURN] - Sai do comando.
  • [ESC] - Retrocede 128 bytes.
  • [TAB] - Avança 128 bytes.
  • [0]-[9],[A]-[F] - Entra com um dado em hexadecimal (obviamente, estando no modo de edição hexadecimal).
  • [Quaisquer Outros Caracteres] - Estando no modo de edição ASCII, as teclas são usadas para alterar o conteúdo dos bytes.

Para editarmos a memória a partir do endereço C000, comandamos:

DM C000

Para editarmos a mesma região com deslocamento de 2:

DM C000,2

Outra forma de edição de memória usa o comando M, que edita a memória a partir de um determinado endereço, tendo a sintaxe:

M [<endereço>]

Caso <endereço> não seja informado, o último endereço usado na edição será utilizado. Ao entrar no modo de edição, temos as seguintes teclas:

  • [SPACE] - Avança um endereço.
  • [BackSpace] - Retrocede um endereço.
  • [RETURN] - Sai do comando.
  • [0]-[9],[A]-[F] - Entra com um dado em hexadecimal.

Este comando não possui a forma de entrada ASCII. Para editarmos a memória a partir de C000, usamos

M C000

O comando S é uma variação do comando M, a diferença é que, ele muda o layout para facilitar a entrada hexadecimal. Obviamente faz mais sentido para micros com teclado destacado, porém até mesmo micros sem o teclado destacado tem seu layout alterado conforme mostrado abaixo:

Teclado:

7 8 9 0
U I O P
J K L Ç
M , . /

Equivalência:

7 8 9 A
4 5 6 B
1 2 3 C
0 F E D

Sua sintaxe é idêntica à do comando M:

S [<endereço>]

valendo as mesmas observações feitas acima.

Modo de Exibição Contínua de Bloco de Memória

Existem 3 comandos que efetuam uma listagem contínua de um bloco de memória determinado pelo usuário. Os comandos D, P e V que tem a seguinte sintaxe:

D <endereço inicial>[,<endereço final>]
P <endereço inicial>[,<endereço final>]
V <endereço inicial>[,<endereço final>]

Os três comandos funcionam da mesma maneira, listando a partir do <endereço inicial> 16 bytes, caso não seja especificado <endereço final>. A diferença está no dispositivo ao qual se dará a saída:

  • D - A exibição é feita em vídeo.
  • P - A listagem é gerada na impressora.
  • V - A listagem de um endereço da VRAM tem sua saída gerada na impressora.

Os três comandos podem ter seu comportamento alterado com o comando C, se executado previamente. C tem a sintaxe:

C <modo>

onde <modo> pode ser:

  • 0 - Apresentação em hexadecimal e ASCII, em linhas com 4 bytes cada.
  • 1 - Idem, porém em linhas com 16 bytes (para 80 colunas ou impressora).
  • 2 - Apresentação em hexadecimal, em linhas com 8 bytes, apresentando soma dos bytes com a parte menos significativa do endereço.
  • 3 - Item, porém não soma a parte menos significativa do primeiro endereço da linha.

Para dar uma amostra, vamos dar o display de 16 bytes na tela a partir do endereço C000, nos 4 modos de listagem disponíveis. Comande:

C0
D C000
C1
D C000
C2
D C000
C3
D C000

Para listar do endereço C000 ao endereço C0FF, no modo de 16 bytes por linha:

C1
D C000,C0FF

Edição em Modo Especial (Tela/Disco)

O comando SCR permite listar e alterar a memória de uma maneira diferente, ao invés de um conjunto de bytes, o Mega Assembler entra em modo SCREEN 2 e permite listar um bloco de memória como se fosse uma tela gráfica. Isto é util para procurar/alterar telas que se encontram em RAM. A sua sintaxe:

SCR <endereço inicial>,<dx>,<dy>[,<modo>]

onde <endereço inicial> é o endereço onde deve começar a suposta tela gráfica, <dx> e <dy> determinam a dimensão da tela a ser exibida, e <modo> pode assumir 0 para edição horizontal, ou 1 para edição vertical.

Após entrar no modo de edição, as seguintes teclas estão disponíveis:

  • [CURSOR LEFT]/[CURSOR RIGHT] - Avança ou retrocede um byte na memória.
  • [CURSOR UP]/[CURSOR DOWN] - Avança ou retrocede um bloco de tamanho <dx> x <dy> na memória.
  • [RETURN] - Entra no modo de edição.
  • [TAB] - Mostra o endereço atual.
  • [ESC] - Liga ou desliga a moldura 2x2.
  • [CTRL]+[STOP] - Sai do comando.

Entrando no modo de edição, um quadro ampliado do bloco em destaque será mostrado junto com um cursor. E as seguintes teclas estarão disponíveis.

  • [CURSOR] - Movem o cursor.
  • [SPACE] - Inverte o ponto sob o cursor.
  • [RETURN] - Sai do modo de edição.
  • [CTRL]+[STOP] - Cancela modificações feitas.
  • [I] - Inverte o bloco 2x2 inteiro.
  • [SHIFT]+[HOME] - Apaga o bloco 2x2 inteiro.

Por exemplo, o comando

SCR 1BBF,1,1

mostra um display da memória, de um caractere a partir do endereço &H1BBF. Lembrando que &H1BBF é o endereço da RAM que contém os caracteres (não podem ser editados, apenas mostrados), para que isso funcione, o comando PAGE deve ser acionado colocando a página 0 no slot 0, caso contrário, ele trará o endereço &H1BBF da página 0 da RAM selecionada no boot (PAGE 0).

Para editarmos este alfabeto por este modo, podemos fazer exatamente como no comando CALL EDITOR mostrado acima, copiar o banco para uma área de RAM e iniciar a edição.

O comando ZAP permite editar setores do disco, tendo o funcionamento muito similar ao comando DM, porém trabalhando com setores e não blocos de memória. Sua sintaxe:

ZAP <setor inicial>[,<deslocamento>]

onde, <setor inicial> é o setor ao qual desejamos iniciar a edição/display, e o deslocamento é a posição do cursor no setor (variando de &H000 a &H1FF).

As mesmas teclas do comando DM estão disponíveis para o ZAP além de

  • [CTRL]+[W]

que gava o setor corrente em disco. Adicionalmente, ao mudar de setor, o Mega Assembler pede confirmação para gravar o mesmo (mesmo que não alterado).

ZAP 0

Edita o setor de BOOT do disco corrente.

Arquivos

Para salvarmos um bloco de memória em formato binário, podemos usar o comando SAVE, com a seguinte sintaxe:

SAVE [<dispositivo>:]<arquivo>,<endereço inicial>,<endereço final>,<endereço de execução>

onde, <dispositivo> pode ser um dos dispositivos válidos para o MSX (CAS:, A:, B:), <arquivo> um nome de arquivo respeitando os padrões do dispositíio, <endereço inicial>,<endereço final> e <endereço de execução> são equivalentes a um comando BSAVE do MSX-BASIC. O arquivo será gravado num formato que permite a leitura por um comando BLOAD no BASIC, além do comando LOAD,B no Mega Assembler, por exemplo:

SAVE A:PROG1.BIN , C000, C0FF, C000

Salva o conteúdo de memória de C000 a C0FF, na unidade A:, com o nome PROG.BIN, e tendo como endereço de entrada C000.

Para carregar um arquivo binário para a memória, via o Mega Assembler, temos o comando LOAD,B, que funciona de modo análogo ao BLOAD do MSX-BASIC, porém não permite especificar um deslocamento. Sua sintaxe:

LOAD [<dispositivo>:]<arquivo>,B

Onde <dispositivo> e <arquivo> são idênticos ao comando SAVE acima, o parâmetro ",B" é obrigatório, pois o comando LOAD sem ele busca por um fonte em assembly (veja o modo Assembler). Exemplo:

LOAD A:PROG1.BIN,B

Carrega o conteúdo do arquivo PROG1.BIN nos respectivos endereços de seu HEADER.

Manipulação da Memória

Podemos buscar uma string ou um conjunto de bytes pela memória usando o comando SH, com a sintaxe:

SH [<endereço>],<byte>[,[<byte> ... [,[<byte>]]]

Onde <endereço> é o endereço inicial de busca, se não informado, continua da última busca realizada, <byte> são uma sequencia de bytes separados por vírgula que devem ser buscados na memória (duas vírgulas seguidas funcionam como um caracter coringa, por exemplo 3A,,FF busca por 3A,01,FF, 3A,CC,FF, ou seja, ",," pode ser qualquer número.). Outra sintaxe válida:

SH [<endereço>],'<string>'

<endereço> tem a mesma função da sintaxe anterior e '<string> indica uma cadeia de caracteres a ser procurada, atente apenas para que após o apóstrofo, deve ser informada uma cadeia de no mínimo dois caracteres.

Em ambas as formas, assim que a sequência é encontrada a busca finaliza e o endereço inicial da cadeia é mostrado na tela, além do possível deslocamento. Exemplo

SH C000,'MSX RULES

Supondo que em C050 temos uma sequência que corresponda a MSX RULES, este endereço será mostrado, com deslocamento 0.

SH C000,3A,,FF

Supondo que tenhamos uma sequência 3A 55 FF em C060, o comando mostrarir C060 como endereço encontrado, com delocamento 0.

Podemos gravar uma string em um endereço de memória com o comando MS, com a seguinte sintaxe:

MS <endereço>,[<deslocamento>],'<string>

Onde <endereço> e o ponto inicial para salvar a sequência contida em <string> usando um deslocamento especificado por <deslocamento>. Por exemplo:

MS C070,5,'MEGA ASSEMBLER

Irá colocar a string MEGA ASSEMBLER no endereço C070, porém usando um deslocamento de 5 bytes, veja o resultado abaixo:

Para transferirmos um bloco de memória entre dois endereços, contamos com o comando T, cuja sintaxe é:

T <endereço inicial>,<endereço final>,<endereço destino>

onde <endereço inicial> corresponde ao inicio do bloco que desejamos copiar, que termina em <endereço final>, e <endereço destino> é o destino de onde os dados serão copiados. Exemplo:

T C000,C0FF,D000

Copia um bloco de memória que vai do endereço C000 até o endereço C0FF para o bloco D000.

Também temos a possibilidade de preencher um bloco de memória, para isso o comando F deve ser utilizado, com a seguinte sintaxe:

F <endereço inicial>,<endereço final>,<byte>

onde <endereço inicial> e <endereço final> correspondem ao bloco de memória que vamos preencher, e <byte> o valor que será inserido em cada posição do bloco. Por exemplo:

F C000,C0FF,2A

Vai preencher a área de C000 a C0FF com o byte 2A.

Depuração / Trace

Podemos executar um programa em memória usando o comando G, que permite executar o programa até o final, ou até encontrar um de no máximo dois break-points para depuração. Sua sintaxe:

G <endereço>[,<break point 1>[,<break point 2>]]

Onde <endereço> é o endereço de entrada do programa, e <break point 1> e <break point 2> são pontos de parada onde a execução será pausada e os registradores serão mostrados. Atenção, quando atingir um <break point> para retomar a execução, o seguinte procedimento deve ser adotado:

As páginas de memória devem estar apontadas para ROM - MEGA ASSEMBLER - RAM - RAM.

Devemos dar um salto para o endereço 4010 (JP 4010).

Para executarmos um programa que se encontra em C000, tendo um ponto de parada em C006, usamos:

G C000,C006

Podemos manipular os registradores com o comando X, com a sintaxe:

X[<registro>]

Onde registro é opcional, se não informado, todos os registradores são mostrados, se for informado um registrador, o mesmo e apresentado para ser editado. Os registradores válidos para este comando são: A, F, B, C, D, E, H e L para os respectivos registradores, X, Y e S para os registradores IX, IY e SP respectivamente. Após a edição, o comando passa para o próximo registro sequencialmente, para interromper a edição basta pressionar [RETURN] para encerrar o comando. Exemplo, para alterarmos os registradores a partir do par BC até o par DE:

XB

O comando L permite disassemblar uma área de memória na tela, tendo como sintaxe:

L[P] [<endereço inicial>[,<endereço final>]]

Todos os parâmetros são opcionais, P direciona a saída para a impressora, <endereço inicial> e <endereço final> são respectivamente os endereços de início e fim da área que se deseja disassemblar. Caso não sejam informados endereços, serão disassembladas 10 linhas a partir do último endereço disassemblado, se P não for informado, a saída é pela tela. Exemplo:

L 0000

Disassemblara as 10 primeiras linhas do início da ROM do MSX na tela.

Modo Assembler

O modo assembler nos permite entrar com programas fontes em Assembly e compilá-los para código objeto, o assemblador é extremamente simples e limitado, porém muito fácil de manipular, pois conta com o editor do MSX-BASIC para poder editarmos os fontes, com isso, todos os atalhos e alguns comandos funcionam de maneira análoga ao MSX-BASIC, facilitando para usuários iniciantes. O formato de entrada de programas Assembly e feito linha a linha, que é dividida em cinco blocos, a saber:

NN   Label:   Instrução   Operando        ;Comentário

onde

  • NN - É o número da linha (obrigatório).
  • Label: - Indica determinada instrução ou posição de memória.
  • Instrução - Qualquer instrução válida do Z80, ou pseudo-instrução do Mega Assembler.
  • Operando - Valor ou código que é necessário para algumas instruções.
  • Comentário - Qualquer observação sobre o programa.

Temos as seguintes pseudo-instruções do Mega Assembler

  • ORG - Indica a posição de memória onde o código objeto será gerado. (ORG <endereço>).
  • DEFB - Coloca o byte especificado na memória. (DEFB <byte>).
  • DEFW - Coloca a palavra (2 bytes) especificada na memória. (DEFW <word>).
  • DEFM - Coloca o texto na memória. (DEFM '<string>').
  • DEFS - Reserva espaço na memória com o número de bytes indicado. (DEFS <número de bytes>).
  • EQU - Indica que o label da linha corresponderá ao valor indicado. (label: EQU <endereço>).

onde

  • <byte> pode ser um byte expresso em número, ou um caracter envolto em ''.
  • <word> pode ser dois bytes ou um label.

O Assemblador trabalha com números em sua forma decima (diferente do monitor que trabalha por padrão com hexadecimal), para indicar números em hexadecimal, um H deve ser colocado à direita do mesmo (se o número iniciar com A, B, C, D, E ou F, um 0 deve ser colocado à esquerda). Números binários devem ser expressos com um B à sua direita.

Comandos de Edição de Programas Fontes

Assim como no MSX-BASIC, o assembler conta com o comando NEW, que apaga o programa fonte existente na memória. Sua sintaxe é simplesmente:

NEW

Como os programas em Assembly devem ter números de linha, o Mega Assembler possui comandos idênticos ao MSX-BASIC para numeração, renumeração e listagem de programas fonte. O comando AUTO cria as linhas automaticamente, com a seguinte sintaxe:

AUTO [<linha inicial>.[<incremento>]

Onde o editor irá gerar linhas a partir de <linha inicial>, acrescidas de <incremento> a cada nova linha concluída com [RETURN].

Podemos listar o conteúdo de um programa fonte usando o comando LIST que aceita a seguinte sintaxe:

LIST [<linha inicial>[-<linha final>]]

ou

LLIST [<linha inicial>[-<linha final>]]

A diferença entre LIST e LLIST é que a saída do primeiro ocorre em tela, e a do segundo em impressora. As formas usuais do MSX-BASIC são aceitas, como LIST, LIST <linha inicial>, LIST <linha inicial>-<linha final>, LIST -<linha final>.

Podemos modificar a numeração das linhas usando RENUM, da mesma forma que no MSX-BASIC.

RENUM [<nova linha>,[<linha antiga>[,<incremento>]]]

Onde <nova linha> é o novo número que vai ser adotado a partir da <linha antiga> (se não informado, começa na primeira linha), com salto de linha baseado no <incremento>.

Por fim, podemos excluir linhas com o comando DELETE, cuja sintaxe:

DELETE <linha inicial>[-<linha final>]

é idêntica à do MSX-BASIC.

Manipulação de Arquivos Fonte

Podemos salvar um arquivo em disco ou fita k-7 com o comando SAVE, tendo a sintaxe:

SAVE [<dispositivo>:]<arquivo>

Onde dispositivo pode ser qualquer válido para gravação de arquivos, e <arquivo> deve respeitar os limites para nome de arquivo do dispositivo.

SAVE PROG.ASM

salva o programa fonte corrente no dispositivo corrente, com o nome PROG.ASM. Perceba que o arquivo é gravado num formato compacto, próprio do Mega Assembler, não sendo possível a leitura direta por outro editor de texto ASCII puro.

Para recuperarmos um programa gravado com SAVE, usamos o comando LOAD, com a sintaxe:

LOAD [<dispositivo>:]<arquivo>

Sendo seu parâmetros idênticos aos do SAVE.

LOAD PROG.ASM

carrega o programa PROG.ASM do dispositivo corrente para o editor assembler. O LOAD não é capaz de carregar arquivos ASCII puros, somente os no formato gravado pelo próprio SAVE do Mega Assembler.

Temos também o comando MERGE para juntar dois programas na memória, um que já se encontra na mesma e outro em fita, cuja sintaxe remete ao MSX-BASIC:

MERGE "[<dispositivo>:]<arquivo>"

onde <dispositivo> e <arquivo> tem definição idêntica aos comandos SAVE e LOAD. Os cuidados para fazer o MERGE são os mesmos do MSX-BASIC, deve se atentar para nao haver sobreposição de linhas. Outro detalhe para ficar atento, o MERGE só funciona com fita K-7, e o nome do arquivo deve obrigatoriamente estar entre aspas.

MERGE "A:PROG2.ASM"

Junta o programa PROG2.ASM com o que está corrente na memória.

Por fim, temos o comando MAP que mostra o endereço inicial e final do programa fonte, sua sintaxe é espartana:

MAP

Comandos de Busca e Substituição

Alguns comandos úteis para buscar textos no programa fonte, como o comando SEARCH, que possui a seguinte sintaxe:

SEARCH <string>

onde <string> é uma cadeia de caracteres. SEARCH busca pela cadeia informada e lista as linhas que contém a mesma, o program possui uma outra forma que é

LSEARCH <string>

que funciona de maneira análoga, porém descarregando os resultados na impressora. O comando tem uma particulariedade, o espaço entre o comando e a string a ser procurada, conta como parte da string, ou seja

SEARCH MSX

equivale a

SEARCH " MSX"

Existe um comando equivalente ao SEARCH, porém busca apenas por linhas iniciadas pela string de busca, este comando é o FIND, cuja sintaxe é:

FIND <string>

Por conta desta particulariedade, ele é mais rápido que o SEARCH, sendo ideal para busca LABELS. Nos exemplos abaixo, perceba o efeito do uso de espaço entre o comando e a string de busca, mencionado no comando SEARCH, mas que vale também para o FIND.

Para completar este set de instruções, temos a instrução CHANGE, que substitui todas as ocorrências de uma string por outra, sua sintaxe:

CHANGE '<string1> '<string2>

Onde <string1> e <string2> são cadeias de caracteres, onde a primeira será usada em substituição à segunda.

Opções para Montagem/Assemblagem do Programa Fonte

O único comando para assemblar o código fonte, é o comando A, o mesmo possui a seguinte sintaxe:

A [NUPOIRSDH/<offset>]

As opções tem os seguintes efeitos:

N - "compila" o programa e não lista o número das linhas. Compila está entre aspas, pois na verdade apenas uma pré-compilação é feita, sem gerar código objeto, para gerar o código objeto efetivamente você deve combinar esta opção com alguma outra, como por exemplo, a opção O, isso vale para os próximos comandos também.

U - "compila" porém não lista o programa.

P - "compila" o programa e gera a listagem na impressora.

O - gera o código-objeto (a partir do endereço informado em ORG). Esta opção efetivamente compila e gera os códigos binários no endereço correspondente, efetivamente gerando um programa que pode ser executado.

I - gera código objeto e o armazena em fita k-7, o código resultante pode ser lido pelo comando R explicado no final deste bloco. Atente para o fato que o comando I não pede nome de arquivo, nem permite que o mesmo seja informado, assim como o comando R para carregar.

R - lista  uma referência dos labels e os endereços onde foram definidos após "compilar" o programa. O exemplo abaixo mostra a "compilação" do mesmo programa usado nos exemplos anteriores, com o comando ANR.

S - lista os labels em ordem alfabética após "compilar" o programa fonte.

D - lista os labels após "compilar" o programa fonte. Basicamente é o mesmo comando que o anterior, porém os labels são listados em ordem de endereço nos quais foram definidos.

H - lista os labels na impressora.

/<offset> Gera o código objeto (nas opções como O e I) para o endereço definido em ORG + <offset>.

Para recuperar da fita um código objeto gerado pela combinação AI, utilizamos o comando R, com a seguinte sintaxe espartana:

R

A fita já deve estar posicionada no ponto onde foi iniciada a gravação.

Erros de Montagem/Compilação

Caso o assemblador encontre algum erro no código fonte, o mesmo será mostrado da seguinte forma.

<linha> <código do erro> <posição relativa> linha com erro.

Onde o código do erro pode ser um dos seguintes:

  • D - número relativo menor que -128 ou maior que 128. Ocorre comumente em instruções JR e instruções que usam os pares IX e IY para indexação.
  • F - erro de sintaxe, uma linha muito grande, um erro de escrita ou estrutura da linha errada.
  • M - label não único, ocorre ao se definir o mesmo label mais de uma vez.
  • U - label desconhecido, o fonte faz referência a um label que não foi definido.
  • Q - instrução do Z-80 ou pseudo código não existente.
  • O - operando inadequado, inválido para a instrução ou inexistente.

Além destes erros, se não for definido um endereço de assemblagem pelo pseudo comando ORG, e for iniciada uma compilação que gera código executável (O ou I), uma mensagem de 'falta de memória' será emitida pelo assemblador. Este erro pode acontecer caso o endereço de montagem for menor que &H4000 ou maior que &HEBFF.

Exemplo de Edição, Compilação e Execução de um Programa

Vamos imaginar o seguinte programa em Assembly:

CHPUT   EQU A2
        LD HL,PRINT
START   LD A,(HL)
        AND A
        RET Z
        CALL CHPUT
        INC HL
        JR SALT
PRINT   DB 'MEGA ASSEMBLER'
        DB 0

Vamos estudar a inserção deste programa no editor. Antes de mais nada, devemos atentar que o editor/assemblador, trabalha por padrão com números decimais, para incluirmos um número hexadecimal, devemos terminar o mesmo com 'H', adicionalmente, se o número comecar por A, B, C, D, E ou F, um zero à esquerda da letra se faz necessário, então, teriamos as seguintes mudanças no código fonte:

CHPUT   EQU 0A2H
        LD HL,PRINT
START   LD A,(HL)
        AND A
        RET Z
        CALL CHPUT
        INC HL
        JR SALT
PRINT   DB 'MEGA ASSEMBLER'
        DB 0

Adicionalmente, devemos definir um endereço de montagem para a geração do código executável, vamos definir, por exemplo, a partir de &HC100. Temos que incluir a linha ORG 0C100H no início do programa.

        ORG 0C100H
CHPUT   EQU 0A2H
        LD HL,PRINT
START   LD A,(HL)
        AND A
        RET Z
        CALL CHPUT
        INC HL
        JR SALT
PRINT   DB 'MEGA ASSEMBLER'
        DB 0

O mega assembler (assim como a maioria dos assembladores) necessita de uma marca para indicar o fim do código fonte, esta marca é o pseudo comando END, que devemos colocar na última linha do fonte.

        ORG 0C100H
CHPUT   EQU 0A2H
        LD HL,PRINT
START   LD A,(HL)
        AND A
        RET Z
        CALL CHPUT
        INC HL
        JR SALT
PRINT   DB 'MEGA ASSEMBLER'
        DB 0
        END

É exigido que os labels no Mega Assembler terminem com ":" (alguns assembladores não exigem esta situação). Então o código final, antes de ser inserido ficaria da seguinte forma.

        ORG 0C100H
CHPUT:  EQU 0A2H
        LD HL,PRINT
START:  LD A,(HL)
        AND A
        RET Z
        CALL CHPUT
        INC HL
        JR SALT
PRINT:  DB 'MEGA ASSEMBLER'
        DB 0
        END

Porém, o Mega Assembler, tem um editor similar ao do MSX-BASIC, e números de linha são obrigatórios tanto para a entrada do programa quanto para as operações de listagem e compilação. Para isso podemos usar o comando AUTO ,. Então entramos com:

> AUTO 100, 10

Em seguida vamos inserindo as linhas conforme a última versão já preparada para o Mega Assembler:

100         ORG 0C100H
110 CHPUT:  EQU 0A2H
120         LD HL,PRINT
130 START:  LD A,(HL)
140         AND A
150         RET Z
160         CALL CHPUT
170         INC HL
180         JR SALT
190 PRINT:  DB 'MEGA ASSEMBLER'
200         DB 0
210         END

Com o código digitado, convém salvar o mesmo para um arquivo, assim, caso ocorra algum problema, temos como recuperar o fonte.

> SAVE PROG.ASM

Após o programa ser salvo, podemos efetuar uma pre-compilação (sem geração de código executável) para verificarmos a possibilidade de haver algum erro de sintaxe em nosso programa, podemos, para tanto, usar o comando:

> AN

Não tendo erro, simplesmente podemos compilar e gerar código executável, com o comando:

> AON

Para nos certificarmos que o programa está em memória, podemos ou consultar seus códigos em linguagem de máquina a partir do endereco &HC100 com:

> D C100,C11B

ou podemos ver um disassembly do mesmo, com:

> L C100

Atente que o monitor usa valores hexadecimais por padrão, não sendo necessário o 0 antes da letra, nem o H após o número. Perceba também que devemos tomar cuidado com a interpretação das listagens disassembladas, veja que a partir do endereço C10C, os códigos não fazem sentido, pois na verdade, neste ponto apenas definimos a string MEGA ASSEMBLER, fato que pode ser visto no comando anterior.

Se quisermos testar o programa (lembrando que já salvamos o fonte), podemos simplesmente usar o comando:

> G C1000

Perceba que quando o programa foi executado com o comando acima, coisas malucas podem ter acontecido, como a impressão de várias mensagens, impressão de códigos desconexos, impressão da mensagem e imediato reset do equipamento, etc. Por que isso ocorre? O mega assembler deveria respeitar o comando RET Z, que o instrui a retornar ao ponto de chamada, porém, o mesmo não lida muito bem com este retorno bagunçando o sistema, não sei se trata-se de um bug, ou falta de entendimento da minha parte. Já tentei dispor as páginas de uma maneira melhor, mas mesmo assim o problema continua ocorrendo. Podemos definir um ponto de parada, por exemplo antes de executar o comando CALL CHPUT, para tanto, podemos ver na listagem do comando AON, qual o endereço desta instrução.

ou, se a instrução fosse precedida por um label, como LD A,(HL) usar o comando:

> AOS

Para gerar uma lista de labels (perceba que LD A,(HL) está precedido pelo label START.

Com o endereço de execução do comando CALL CHPUT, podemos comandar definir um BREAK POINT usando o comando:

> G C100,C106

Assim o programa vai parar imediatamente antes do CALL CHPUT e podemos estudar ou alterar os registradores para depois continuarmos a execução (o que o mega assembler também não executa muito bem).

Podemos salvar o binário do executável, bastando apenas saber o endereço inicial e final, e darmos o comando:

> SAVE PROG.BIN,C100,C11B

Para recuperar este binário no próprio Mega Assembler, comandamos:

> LOAD PROG.BIN,B

Para voltarmos ao MSX-BASIC e executar como sub-rotina do mesmo, basta comandar:

> BASIC

e no MSX BASIC usar:

DEFUSR=&C100 : A=USR(0)

Isso funciona pois o código fonte, após a compilação, mesmo retornando ao MSX-BASIC, se encontra na memória. Caso a memória tenha sido alterada, ou o micro tenha sido resetado, simplesmente podemos usar:

BLOAD "PROG.BIN", R

ou

BLOAD "PROG.BIN" : DEFUSR=&HC000

e quando necessitar chamar a rotina:

A=USR(0)

Isto finaliza um exemplo praticamente completo de entrada e execução de um código fonte.

Usando o Mega Assembler como Programa Monitor para o Livro Linguagem de Máquina

Logo no primeiro capítulo, após a listagem do programa monitor (página 19), o livro sugere um pequeno teste, usando os recursos de edição e dump de memória, vamos ver como fazer o mesmo exemplo a partir do Mega Assembler.

É solicitada a entrada dos seguintes dados: A4, B7, 04, 12, 06, 71, FD, A1, 00, CE em &HC000 a partir do comando M (do monitor).

Para efetuarmos o procedimento, antes devemos carregar o Mega Assembler (se você acabou de dar boot no sistema, o programa já se encontra no modo monitor) usando o comando:

CALL START


Em seguida podemos usar o seguinte comando para abrir a tela de edição de memória do Mega Assembler:

DM C000

A partir deste ponto, basta informarmos os códigos hexa do livro, um após o outro (lembre-se de mudar da edição ASCII padrão para a edição HEXA, com [SELECT]).


Após a entrada dos códigos, saia com [RETURN].

O livro pede para dar um DUMP na memória, podemos usar o próprio DM, ou então:

C1

D C000

Que vai mostrar a listagem de uma outra forma, sem entrar no modo de edição novamente.

O livro prossegue com listagens sempre no mesmo formato, dispostas em colunas, onde temos, respectivamente, o endereço corrente, os códigos do comando e parâmetros, os mnemônicos e seus parâmetros e um comentário ao final. O Mega Assembler, lida de uma maneira simples com a entrada destas linhas, dando a liberdade tanto de usar os códigos como sugerido pelo monitor do livro, ou como listagem em assembly (como eu recomendo) incluindo comentários, labels e facilidades de edição.

Por exemplo, na página 28 temos o seguinte programa:

C000    2600    LD      HL,00H  ;carrega H com 0
C002    1E2A    LD      E,2AH   ;carrega E com 2AH
C004    44      LD      B,H     ;copia H em B
C005    4B      LD      C,E     ;copia E em C
C006    C9      RET             ;volta ao BASIC

Podemos entrar com estas informações de duas maneiras, com o comando DM ou o comando M como foi mostrado no início, ou entrando com um código fonte (como foi mostrado no tópico anterior (Exemplo de Edição, Compilação e Execução de um Programa. Resumidamente:

> AUTO 100
    ORG 0C000H
LD H,00H   ;carrega H com 0
LD E,2AH   ;carrega E com B
LD B,H     ;copia H em B
LD C,E     ;copia E em C
RET        ;volta ao BASIC

Não se esqueça de dar [RETURN] a cada linha, também verifique se já existe outro programa na memória com LIST, caso exista, salve com SAVE, e caso já tenha sido salvo, ou pode ser descartado, limpe a área de edição com NEW. Dica: Para limpara a tela, use [CTRL]+[L], aliás, todos os atalhos de teclado que podem ser usados no MSX-BASIC, podem ser usados também no editor do Mega Assembler.

É de boa prática usar a tecla [TAB] para separar e alinhar/identar os comandos, facilitando assim sua leitura. Como dica, recomendo que se use o modo de 80 colunas, para melhorar mais ainda a apresentação.

Para obtermos a listagem disassemblada e o dump de memória solicitados pelo livro, devemos antes de mais nada gerar o código fonte com o comando AON.

Para a listagem disassemblada: 

L C000

e para o DUMP

D C000

Downloads

  • megaasm.dsk: Disco contendo as 3 versões mais conhecidas do Mega Assembler em .ROM, a versão .COM do Adriano, o manual da Cybertron em .PDF e o manual da versão 1.0b em TXT, além do Execrom.
  • megasm.rom: Acesso direto a versão usada pelo tutorial.