Obtendo os padrões de fonte do MSX
No último artigo, aprendemos como modificar um único caractere do tipo de fonte original do MSX, e também descobrimos as diferenças entre o caractere da Screen 0 e Screen 1, mas agora o que queremos fazer é criar nosso próprio tipo de fonte pessoal o mais artístico possível. Coisa chique!
Antes de começarmos a projetar nosso padrão de personagem, pensei “não seria legal ter o padrão original para que pudéssemos ver o que estamos realmente fazendo?”. Claro que não sou designer de fontes profissional, e o melhor que posso fazer é esticar algumas vogais e adicionar algumas curvas nas consoantes, e para isso, definitivamente preciso ter o caractere original exibido na tela para referência.
De onde vêm as fontes dos caracteres?
Agora, há algo que sempre dei como certo e nunca imaginei que seria algo perceptível, mas todo computador vem com um padrão de fonte predefinido armazenado em algum lugar, caso contrário, não imprimiria nada legível na tela.
Numa maquina MSX, como informado na pagina wiki do site MSX.org, o padrão de fonte é copiado da ROM principal para a VRAM toda vez que invocamos a rotina Screen para definir o modo de tela, substituindo qualquer padrão atualmente armazenado na VRAM. Enquanto na Tela 0 (modo texto 40×24) o padrão ocupa 2 KB de VRAM de 0800H a 0FFFH, e na Tela 1 (modo texto 32×24) o padrão também utiliza 2 KB de VRAM de 0000H a 07FFH, e esta informação é importante porque estamos prestes a copiar o padrão do endereço definido pelo modo de vídeo para a RAM, para que possamos imprimir os padrões na tela e fazer algumas coisas legais com isso.
Levei algum tempo para descobrir o que devo fazer para mover os dados de padrão entre o MSXPen e o TinySprite, pois essas são as duas ferramentas que prefiro usar no meu Chromebook quando faço minha pesquisa. Existem várias outras ferramentas por aí que rodam no Windows, Linux, macOS, e até mesmo em máquinas MSX reais, mas eu quero evitar qualquer coisa que não possa ser usada prontamente pelo navegador, e eu até posso criar algumas ferramentas online no futuro para tornar as coisas mais fáceis, mas isso é algo para outro dia.
À principio, eu criei esse código que copia os dados do endereço 0x8*49 (Padrão do caractere 0) para a RAM, depois usa o comando pedaço
para comparar cada bit dos bytes do padrão e imprimir “0” ou “1” com base no resultado do comando, e é importante notar que não estamos armazenando os resultados em nenhum outro lugar que não seja a tela real. Usando o emulador MSXWeb do site MSXPen podemos pressionar a combinação de teclas “alt+c” para copiar o conteúdo da tela para a área de transferência (sim, eu sou tão velho, e sim, é o nome real desta função) então nós pode colá-lo em um arquivo de texto.
O código com os comentários usuais é o seguinte:
CHGMOD: equ 0x005f ;Chamada de BIOS para mudar o modo de tela CHPUT: equ 0x00a2 ;Imprime os pontos de conteúdo por A na tela CLS: equ 0x00C3 ;Limpa a tela. chama XOR A antes para limpar o flag Zero ERAFNK: equ 0x00CC ;Remove as teclas de função da tela CHGET: equ 0x009F ;Espera que uma tecla seja pressionada LDIRMV: equ 0x0059 ;Copia blocos A do endereço HL da VRAM para endereço DE na RAM org 0xc000 ; Armazenando o programa na página 3 main: ld a,1 ;Definimos o modo de tela 1 carregando o valor em A call CHGMOD ;depois chamamos CHGMOD para alterar o modo de vídeo chame ERAFNK ;Removendo as teclas de função para que não seja copiado através de alt+ c ld hl,0x8*49 ;Carregando em HL a posição do primeiro padrão. Cada padrão usa 8 bytes ;então multiplicamos 8 pelo padrão inicial de caracteres na posição 49 = 0 ld de,font ;Carrega em DE a posição onde o padrão VRAM será copiado ld bc,8*74 ;Queremos 74 caracteres copiados, de 0 a "z" chama LDIRMV ;Chama a rotina para copiar dados da VRAM para a RAM ld hl,font ;Carregamos o endereço definido para "font" em HL ld bc,74 ;depois colocamos o contador em BC para o principal loop ld d,49 ;e armazene em D o valor do primeiro caractere (0) para imprimir depois call loop ;Chamando o loop principal ret ;depois retornando para BASIC quando BD diminuir para 0 loop: aperte BC ;Vamos enviar BC para o stack push DE ;seguido por DE XOR A ;Isso deve redefinir o sinalizador Zero CALL CLS ;assim podemos chamar CLS para limpar a tela ld b,8 ;Carregar 8 em B para iterar em cada byte do padrão de caractere pop DE ;Vamos traga DE de volta da pilha ld a,";" ;Carrega o caractere ";"em A chamada CHPUT ;então imprime antes de imprimir o caractere atual que está sendo mostrado ld a,d ;Pega o valor de D e carrega em A chamada CHPUT ;e imprime, que corresponde à letra que está sendo iterada ahead inc d ;Diminuímos D ld a,"\r" ;Carregamos o código ASCII para retornar em A call CHPUT ;Em seguida imprimimos para retornar ao início da linha ld a,"\n" ;em seguida carregamos o ASCII código para nova linha em A chama CHPUT ;depois imprime, movendo o cursor para a nova linha abaixo call iterate ;Chama a rotina iterate que imprime o padrão como binário call CHGET ;Espera que uma tecla seja pressionada após imprimir o padrão de cada letra POP BC ;Traz BC de volta da pilha dec BC ;Decrementa BC LD A,C ;Carrega C em A OU B ;E executa A OR B JR NZ,loop ;Enquanto a flag zero não é levantada repete o loop ret ;Retorna para a rotina principal iterar: ;Esta rotina provavelmente pode ser melhorada usando operações de deslocamento ;mas para manter as coisas simples de entender eu deixei como é o bit 7,(hl) ;Lê th e primeiro bit do byte apontado por HL chama z,zero ;e chama a rotina zero se o resultado for zero chama nz,one ;caso contrário chama a rotina um bit 6,(hl) ;Mesma coisa mas com o próximo bit chama z ,zero ;assim repetimos todo o processo chame nz,one ;em cada bit do by apontado pelo HL bit 5,(hl) ;e chame a rotina apropriada para imprimir call z,zero ;o caractere ASCII relacionado à chamada de valor nz,um bit 4,(hl) chama z,zero chama nz,um bit 3,(hl) chama z,zero chama nz,um bit 2,(hl) chama z,zero chama nz,um bit 1,(hl ) call z,zero call nz,one bit 0,(hl) call z,zero call nz,one ld a,"\n" ;Após o último bit ser verificado adicionamos uma nova linha chamada CHPUT ;e imprimimos ld a ,"\r" ;então adicionamos uma chamada de retorno de carro CHPUT ;e imprimimos inc hl ;Aumenta HL para obter o próximo byte djnz iterate ;E faça isso até que B atinja 0 ld a,"\n" ;No final da iteração adicionamos outra nova linha chamada CHPUT ;e imprimimos RET ;retornando à rotina de loop zero: ld a,"0" ;Carregamos o caractere ASCII "0" em A call CHPUT ;depois imprime RET ;e retorna para o chamador da rotina iterativa one: ld a,"1" ;Carregamos o caractere ASCII "1" em A call CHPUT ;depois imprimimos RET ;abd retorna para o chamador do iterar rotina fonte: DB 0 ;Valor inicial da fonte end main ;Isso finaliza o programa conforme requerido pelo analisador de código MSXPen
No final, isso resultou em um programa bastante simples e funcional, mas de uso muito limitado, a menos que você queira usar um editor de texto para criar seus padrões de fonte em binário.
Mas, podemos fazer algo melhor?
Claro que podemos! Precisamos transferir os padrões de fonte originais do emulador WebMSX para a ferramenta TinySprite, e o formato binário não é útil para este requisito.
A ferramenta TinySprite nos permite importar designs de padrões através de dois métodos diferentes:
- Usando o botão “Load Backup” e colando o backup no campo do widget
Observe que a ferramenta usa um formato de backup proprietário que, embora esteja em modo texto, precisa seguir os parâmetros apropriados, caso contrário resultará no carregamento de lixo nos slots. É assim que um backup do TinySprite se parece:
O cabeçalho descreve o tipo de sprite (MSX1/MSX2), o nome do slot (0123 para este exemplo), então temos o padrão onde os pontos são espaços vazios/transparentes e os números estão relacionados à cor utilizada pelo pixel no posições.
As opções Export Sprites e Load Backup são as opções disponíveis para dar persistência de dados na ferramenta, pois toda vez que o site é fechado ou mesmo recarregado todo o conteúdo é perdido. Não diga que eu não avisei sobre isso!
- Use o "Importar DADOS para o Slot"
Esta opção está disponível através do último ícone do lado direito do campo Slots, aquele com a seta apontando para um monte de páginas que parecem um papel de formulário contínuo dos dias de outrora:
Este é muito útil, pois podemos carregar padrões em um único slot, e é o que usaremos para transferir os dados da saída do WebMSX, mas possui algumas limitações. Não consegui carregar nenhum dado usando formato binário nos slots, e o único formato que funcionou para mim foi em hexadecimal, então é isso que vou usar aqui.
Levei algum tempo para descobrir qual seria a melhor maneira de transformar meus requisitos em um código útil. Eu tentei fazer algumas coisas malucas como armazenar os valores na RAM e depois mudar o modo de tela de volta para o modo de texto 40×24, depois escrever os dados em um dispositivo e mostrar as informações um caractere por vez, mas no final , finalmente cheguei com uma solução que entregava o que eu precisava muito bem! A melhor coisa é que eu poderia encontrar um código de montagem completo que transforma valores decimais em hexadecimais, e isso me economizou muito tempo, para ser honesto! Graças a Deus pelo StackOverflow!
Aqui está o código que imprime todos os padrões para os caracteres entre o número 0 até a letra minúscula “z”:
; bios chamada para imprimir um caractere na tela CHGMOD: equ 0x005f ;Muda o modo de tela WRTVRM: equ 0x004d ;Grava na VRAM no endereco HL o conteudo de A CHPUT: equ 0x00a2 ;Imprime o conteudo de A na tela LDIRMV: equ 0x0059 ;Copies A bloqueia o endereço HL da VRAM para o endereço DE na RAM ERAFNK: equ 0x00CC ;Remove a tecla de função da tela LINL32: equ 0xF3AF ;Largura para SCREEN 1 (padrão 29) CHGET: equ 0x009F ;Aguarda que uma tecla seja pressionado CLS: equ 0x00C3 ;Limpar tela. Requer que o sinalizador Zero seja definido, então XOR A deve fazer o truque org 0xD000 ;Vamos armazenar o programa na página 3 start: ld hl,LINL32 ;Estamos alterando a largura da tela no modo 1 ld (hl),32 ;definindo o valor 32 no endereço ointerd ld a,1 ;depois carrega 1 no registrador A chama CHGMOD ;e chama a rotina CHGMOD chama ERAFNK ;Vamos remover as teclas de função da tela para que não seja copiado junto com os dados ld hl,0x8*48 ; Copiaremos os dados da VRAM do 48º caractere (0), então multiplicamos por ;8 que é o tamanho de cada padrão ld de,font ;Carregando em DE a posição de memória para a fonte ld bc,75*8 ;Queremos copiar 8 bytes de padrões de 75 caracteres, então BC será definido como 75*8 chame LDIRMV ;Chame a rotina para copiar os dados da VRAM para a RAM ld hl,font ;Vamos colocar a posição da RAM onde o padrão foi copiado em HL ld b,5 ;Depois defina 5 em B como o número de páginas com a saída hexadecimal inicial: pressione BC ;Salva BC na pilha ld b,16 ;Carrega 16 em b, que será o contador f ou padrões de caracteres por página ld c,1 ;Carrega 1 em C, que será usado para adicionar uma linha de quebra após 4 linhas de padrões call mainloop ;Chama o loop principal call CHGET ;Espera que uma tecla seja pressionada para controlar a saída pagination xor a ;Levantando o flag Zero chame CLS ;para que possamos limpar a tela chamando a rotina CLS POP BC ;Trazendo BC de volta da pilha djnz initial ;depois percorrendo inicial até que B diminua para zero ret ;fim do programa e retorne para BASIC , esperançosamente mainloop: PUSH BC ;Salvando os valores de BC na pilha ld b,8 ;Carregando 8 em B, que será o contador para loop de chamada de loop ;Chamando a rotina de loop ld a,"\r" ;Adicionamos ASCII código para retorno de carro em A call CHPUT ;então imprimimos ld a,"\n" ;em seguida carregamos a próxima linha em A call CHPUT ;e chama CHPUT novamente, pulando o cursor para o início de uma nova linha pop BC ; Obtendo BC da pilha inc hl ;Incrementando HL para prosseguir para o próximo padrão de caractere ld a,c ;Agora vamos carregar o valor de C em A cp 4 ;E compará-lo com 4, que cuidará da quebra de linha para cada bloco call z,skip4 ;Se o CP 4 originar o sinalizador zero, chamamos a rotina skip4 INC C ;Incrementando o contador em C djnz mainloop ;Então fazendo loop pelo mainloop até que B diminua para zero ret ;o que permite retornar o programa de volta à rotina inicial skip4: ld c,0 ;Carrega 0 em C para redefinir o contador ld a,"\n" ;Então mais uma vez imprimimos new line call CHPUT ;e ld a,"\r" ;carriage return call CHPUT ;na tela, criando uma linha vazia entre blocos de 4 padrões de caracteres ret ;e retorna ao loop mainloop: ld a,"$" ;cada hexadecimal value precisa ser precedido pelo cifrão call CHPUT ;então imprimimos primeiro durante o loop ld a,(hl) ;agora carregamos o valor onde HL está apontando para call NumToHex ;e chamamos o NumToHex para transformar o valor decimal em hexadecimal ld a,d ;O NumToHex retorna os valores em DE, primeiro carregamos D em A chamada CHPUT ;e imprimimos o primeiro dígito do valor hexadecimal l d a,e ;então carregamos E em A chamada CHPUT ;para imprimir o segundo dígito do valor hexadecimal djnz loop2 ;e pular para loop2 até que B diminua para 0, para evitar adicionar uma vírgula no ;final da linha com o padrão data ret ;terminando o loop e retornando para a rotina mainloop loop2: ld a,"," ;o loop2 adicionará uma vírgula entre os valores hexadecimais call CHPUT ;e imprimirá inc hl ;depois aumentará a posição apontada por HL jr loop ; e repita a rotina de loop NumToHex: ;Rotina emprestada de ;https://stackoverflow.com/questions/22838444/convert-an-8bit-number-to-hex-in-z80-assembler ld c, a ;a = number to convert call Num1 ;chama a rotina Num1 ld d, a ;então carrega A em D ld a, c ;e restaura o valor de A de C call Num2 ;chama a rotina Num2 depois desse ld e, a ;e carrega A em E ret ;retorna com número hexadecimal em DE Num1: rra ;rotação de 9 bits para a direita - http://z80-heaven.wikidot.com/instructions-set:rr rra ;gira A para a direita novamente rra ;e novamente rra ; e mais uma vez Num2 ou $F0 ;executa uma operação OR em A e $F0 daa ;o registrador A é corrigido em BCD usando o conteúdo dos sinalizadores ;http://z80-heaven.wikidot.com/instructions-set:daa add a, $A0 ;load $40 em A adc a, $40 ;Ascii hex neste ponto (0 a F) ret ;e retorna para a fonte de rotina NumToHex: db 0 ; use o rótulo "start" como o ponto de entrada end start
O programa pode ser executado no MSXPen a partir desse link, e depois que cada página é impressa, você pode usar Alt+C para copiar o conteúdo para a área de transferência, conforme mostrado aqui:
Em seguida, basta usar a opção “Import DATA into slot” do TinySprite para carregar os padrões. Observe que precisamos inserir apenas 4 linhas de padrões em cada slot, e é por isso que o código adiciona um espaço após quatro linhas com dados. Viu como eu sou uma pessoa legal?
Lave, enxágue e repita até que todos os caracteres sejam carregados em 19 slots da ferramenta TinySprite. Também mudei os nomes de cada slot para facilitar as coisas.
Pode ser um pouco mais fácil?
Sim pode! Basta baixar o arquivo de backup do TinySprite abaixo, copiar seu conteúdo e colar no widget “Carregar Backup” da ferramenta!
Agora temos tudo que precisamos para criar aquele tipo de fonte incrível que sempre quisemos, e carregá-lo de volta em nossas máquinas MSX para imprimir textos incríveis! Mas isso é assunto para outro artigo. Até lá, divirta-se modificando seus padrões de personagem, mas não se esqueça de fazer backup de seu trabalho após terminar!
Relacionado
No último artigo, aprendemos como modificar um único caractere do tipo de fonte MSX original, e também descobrimos as diferenças entre os caracteres da Tela 0 e da Tela 1, mas agora o que queremos fazer é criar nosso próprio tipo de fonte pessoal tão artístico quanto possível. Chique! Antes de começarmos…