Neste número: Conversão Impressora Gráfica, Transferindo programas disco/fita/disco, Gerando sons no MSX, Slots e expansões, Sistema de Gravação cassete no MSX, Menus em SCREEN2, Jawbreaker, Dicas, Matemágica
Conversão Gráfica
Existem dois métodos de se fazer isso: o primeiro usa as tabelas da VRAM e o segundo testa diretamente os pontos na tela. O objetivo deste artigo é fazer uma comparação em termos de velocidade e complexidade dos dois métodos para um determinado tipo de cópia.
Todas as rotinas apresentadas neste artigo foram desenvolvidas e testadas em um micro HOTBIT e uma impressora Mônica da Elebra.
A LPTOUT
Esta é uma rotina importantíssima da ROM do seu MSX, pois é ela quem envia para a impressora o código do caractere a ser impresso. Para podermos utilizá-la corretamente, é necessário o uso de uma pequena subrotina em linguagem de máquina. E, mesmo que você não tenha experiência no assunto, irá entendê-la facilmente.
Existe um registrador do Z-80 chamado 'acumulador' ou, simplesmente, registrador 'A'. A LPTOUT enviará para a impressora o código do caractere que estiver armazenado no acumulador, quando este for chamado através de um CALL, que é análogo a um GOTO do BASIC.
Para armazenarmos no acumulador um determinado valor, fazemos uso da instrução LOAD, que significa 'carregar'. A sintaxe desta instrução é LD A,XX. Feito isso, devemos chamar a rotina com um CALL relativo ao seu endereço de entrada, que, no caso da LPTOUT, é 00A5H.
Em último lugar, deverá haver o retorno ao BASIC, o que é feito quando a instrução RET (RETURN) for executada.
O nosso programa, em linguagem de máquina, ficará assim:
E000 3Exx LD A,xx
E002 CDA500 CALL 00A5H
E005 C9 RET
A primeira coluna é relativa aos endereços; a segunda às instruções em hexadecimal; e a terceira, aos mnemônicos do Z-80.
O endereço inicial da rotina for i definido como E000H por ser uma posição de memória relativamente alta e não requerer maiores cuidados na maior parte dos programas em BASIC.
Para chamarmos a rotina através do BASIC, basta definirmos em uma chamada USR seu endereço de entrada, por meio de um DEF USR.
Feito isso, basta pokearmos em E001H o código do caractere desejado e acessarmos a rotina através de uma declaração USE, como, por exemplo, um G=USR(0).
A Cópia
Para entendermos como o primeiro método funciona, é necessário fazermos um pequeno estudo da VRAM do MSX, quando em SCREEN 2.
Existem duas tabelas fundamentais para o nosso caso. Estas são a de padrões e a de cores.
A tabela de padrões contém a imagem propriamente dita, inicia no endereço zero e termina em 6143.
Os pixels do vídeo estão organizados de acordo com o esquema da figura 1, dispostos horizontalmente em octetos na seguinte ordem: o primeiro é formado pelos pixels relativos às coordenadas (0,0)…(1,7), até o oitavo, que representa a sequência (7,0)..(7,7). O nono octeto é representado por (8,0)..(15,0) e assim sucessivamente até o último octeto, de número 6144, que corresponde ao grupo (248,191)…(255,191).
O programa da listagem 2 usa este algoritmo que agora lhes será explicado em detalhes.
As duas primeiras linhas do programa montam a nossa rotina em linguagem de máquina na memória, a partir do endereço E000H, definindo o endereço de entrada da chamada USR.
Na terceira linha, a cor de fundo da tela é armazenada na variável F% e a linha 60030 faz com que a impressora passe a trabalhar em modo EPSON, ajustando a entrelinha para oito pontos.
A linha 60050 faz com que a impressora entre em modo gráfico, esperando sequências de 192 caracteres.
Os loops fornecem a sequência correta de impressão dos octetos, sendo esses pokeados pelas variáveis J%, S1% e RP%, respectivamente.
É feita, então, a comparação com a cor de fundo, que decide ou não a impressão do octeto.
A chamada da LPTOUT é feita na linha 60090 e o CHR$(1) na linha 60110 faz o avanço da linha, assim que a sequência gráfica tenha sido completada. A última linha faz com que a impressora volte ao modo normal de operação.
Observe que o programa da listagem 2 não copia SPRITES, pois, para isso, seria necessária a consulta das tabelas de formação e cores dos SPRITES.
Jà pelo segundo método, as coisas ficam muito mais fáceis. Além de qualquer coisa ser copiada, a rotina é muito mais simples e objetiva. Basta fazermos uma varredura de todos os pontos do vídeo, através de dois loops e testar se a cor de fundo é igual à cor do ponto. Isso é feito com o auxílio de uma das fun;cões menos utilizadas no BASIC – o POINT – e, nem por isso é pouco eficiente.
Cada ponto aceso na tela irá disparar uma agulha e a entrelinha será alterada para 1 ponto. O teste dos pontos é feito na linha 60070 da listagem 3. O restante é idêntico ao programa anterior.
O primeiro método leva uma pequena vantagem quanto ao tempo de execução, mas convém citar que só é verdadeiro para uma impressão vertical e de dimensões 192×256.
A impressão horizontal já é bem mais difícil de se conseguir, pois o algoritmo de rotação da tabela de padrões é bem complexo, sendo a varredura radicalmente diferente para o primeiro método. Chamaremos cada grupo de oito octetos consecutivos de “grupo local” (Vide figura 2).
O nosso objetivo é tornar os octetos reais em octetos verticais, o que é conseguido agregando-se os bits de mesmo peso de cada um dos octetos reais: o primeiro octeto vertical será formado pela concatenação dos bits mais significativos dos octetos reais, e assim sucessivamente.
Se usarmos um método numérico para tal, a rotina se tornará extremamente lenta. Por isso, faço uso de um método alfanumérico, pois, além de ser muito mais rápido, é compacto.
O programa da listagem 4 faz exatamente isso. A matriz alfanumérica B$, de dimensões 8×1, contém as strings binárias relativas aos oito octetos, sendo transformada na variável U$ em um octeto vertical.
O resultado é armazenado no acumulador, sendo, posteriormente, impresso.
O programa da listagem 5 faz o mesmo tipo de cópia, usando o algoritmo do segundo método, sendo muito mais eficiente. Este programa não passa do mesmo programa da listagem 3 com os loops alterados.
A Ampliação
Para fazermos uma ampliação, o primeiro método se torna extremamente ineficiente.
No caso do primeiro método, se desejarmos uma impressão multiplicada por um fator de dois, teremos que ampliar um ponto dilatando-o na horizontal e na vertical.
A expansão horizontal é trivial de ser feita, pois basta imprimirmos duas vezes seguidas cada octeto. Mas para ampliarmos na vertical, esbarramos em um problema: o código do octeto expandido passará a ter 16 bits e a impressora só tem oito agulhas. A solução é quebrarmos esse código em dois menores de oito bits, enviando primeiro para a impressora os oito bits mais significativos, e, em seguida, os oito menos.
O raciocínio é o mesmo para ampliações de fator 3 em diante (só que o primeiro método só permite ampliações múltiplas de 2). É como se a palavra “MSX” fosse transformada em “MMSSXX”, “MMMSSSXXX”, e assim por diante.
Os programas da listagem 6 e 7 fazem isso pelo primeiro método e os restantes pelo segundo.
O segundo método é muito mais adequado quando se trata de cópias que requerem algoritmos mais sofisticados e são muito mais rápidos e fáceis de serem compilados, pois até o compilador COMP32 é capaz de fazê-lo.
Por último, serão dadas tabelas para auxiliar a alteração do programa básico do segundo método para obtermos cópias ampliadas por um fator n e como fazer a reversão de impressão para cada tipo.
Listagem1:
10 for c=0 to 31
20 h=c*8
30 for l=23 to 8 step -1
40 k=256*l+h
50 for x=7 to 0 step -1
60 j=x+k:print k
70 next x,l,c
Listagem2:
50000 ' COPIA VERTICAL NORMAL (MSX)
50001 ' ANTONIO FERNANDO S. SHALDERS - 1988
60000 defint a-z:for en=0 to 5:read co:poke &he000+en,co:next
60010 data 62,0,205,165,0,201:def usr=*he000
60020 f=peek(&Hf3ae)
60030 poke&hf417,1:lprint chr$(27);"A";chr$(8)
60040 for c=0 to 31:h=c*8
60050 lprint chr$(27);"K";chr$(192);CHR$(0);
60060 for l=23 to 0 step -1:k=256*l+h
60070 for x=7 to 0 step -1:j=x+k:sl=vpeek(j):rp=vpeek(j+8192)
60080 if rp mod 16<>f then poke &he001,255 else poke &he001,sl
60090 g=usr(0)
60100 next x,l
60110 lprint chr$(13);
60120 next c
60130 lprint chr$(27);"A";chr$(13):poke&hf417,0
Listagem3:
50000 ' COPIA VERTICAL NORMAL (MSX)
50001 ' ANTONIO FERNANDO S. SHALDERS - 1988
60000 defint a-z:for en=0 to 5:read co:poke &he000 + en,co:next
60010 data 62,0,205,165,0,201:def usr=&he000
60020 f=peek(&Hf3ea):poke &hf317,1
60030 lprint chr$(27);"A";chr$(1)
60040 for x x=0 to 255
60050 lprint chr$(27);"K";chr$(192);chr$(0);
60060 for y y=191 to 0 step -1
60070 if point(xx,yy)=f then 60090
60080 poke &he001,1:goto 60100
60090 poke &he001,0
60100 g=usr(0)
60110 next y:lprint chr$(10);:nextx x
60120 lprint chr$(27);"A";chr$(13):poke &hf417,0
Listagem4:
50000 ' COPIA HORIZONTAL NORMAL (MSX)
50001 ' ANTONIO FERNANDO S. SHALDERS - 1988
60000 defint a-z:for en=0 to 5:read co:poke &he000+en,co:next
60010 data 62,0,205,165,0,201:def usr=&he000
60020 f=peek(&hf3ea):poke&hf417,1
60030 lprint chr$(27);"A";chr$(8)
60040 for l=0 to 23:k=256*l
60050 lprint chr$(27);"K";chr$(0);chr$(1);
60060 for c=0 to 248 step 8:h=k+c
60070 for x=0 to 7:j=x+h
60080 sl(x)=vpeek(j):rp=vpeek(j+8192)
60090 sl$(x)=right$(string$(8,"0")+bin$(sl(x)),8)
60100 if rp mod16<>f thens l$(x)=bin$(255)
60110 next x
60120 for x=0 to 7:a=x+1
60130 sc$=mid$(sl$(0),a))+mid$(sl$(1),a,1)+mid$(sl$(4),a,1)+mid$(sl$(7),a,1)
60140 sc=val("&B"+sc$)
60150 poke &he001,sc:g=usr(0)
60160 next x,c:lprint chr$(10);next l
60170 lprint chr$(27);"R";chr$(13):poke&hf417,0
Arquivos:
Transfira seus programas de disco para fita e vice-versa
Artigo Original: Marcelo Fontolan, Revisão: Wilson Pilon
Grande parte dos usuários da linha MSX está passando seus arquivos de fita para disco, pois os discos oferecem maior rapidez e segurança no armazenamento de dados.
Quando o programa a ser gravado em disco for em Basic, não teremos dificuldades para efetuar a leitura da fita e gravá-lo no disco.
Em se tratando de programas escritos em assembler, a operação exige um pouco mais de cuidados e atenção, principalmente no que se refere aos endereços.
Visando facilitar este trabalho, publicamos um programa cuja função é ler um programa da fita cassete e gravá-lo no disco ou vice-versa.
Programas que estejam gravados em fita com proteção, ou seja, sem header, não poderão ser transferidos para o disco com auxílio deste programa.
Pensando na necessidade que o usuário possa vir a ter, o programa também possibilita transferências de programas de disco para fita cassete.
O funcionamento do programa é bastante simples.
Funcionamento
Ao completar o carregamento, é apresentado um menu com três opções, que permite selecionar qual a operação que deve ser efetuada.
Cópia de Fita para Disco
No menu, após selecionar a opção 1, prepare o gravador e pressione uma tecla. Será então, lido o header do programa e serão apresentados na tela os endereços e os comandos para leitura do programa da fita e gravação no disco.
Conhecidos os endereços, retorne a fita até o início e posicione o cursor no comando para leitura, pressionando [RETURN]. Será iniciada a leitura.
Terminada a leitura, posicione o cursos no comando para gravação no disco, pressionando, novamente, [RETURN].
O programa lido das fitas encontra-se gravado no disco.
Alguns erros podem acontecer durante este processo, como erro de leitura (problema com a fita, volume, etc.), erro de gravação (disco protegido, não formatado, etc.) e caso o programa não seja do formato assembler.
Todo programa em BASIC tem sua área na memória a partir do endereço &H8000, sendo que alguns programas em assembler também possuem o mesmo início.
Neste caso, haverá a necessidade de carregar o programa após cada transferência.
A necessidade de um novo carregamento será informado pelo próprio programa, através das mensagens 'TECLE [F4]' e 'TECLE [F5]', para os casos em que o arquivo copiado se sobreponha ou não respectivamente, ao copiador.
Cópia de Disco para Fita
Ao solicitar transferência de disco para fita, será apresentado o diretório do disco e solicitado o nome do programa do qual se deseja efetuar a cópia.
Na tela, serão apresentados os comandos para leitura do programa do disco e gravação em fita, já com os endereços.
O cursor deverá ser posicionado no comando para leitura e depois no comando para gravação, prestando-se atenção para que o gravador esteja pronto para efetuar a gravação.
O Programa
O programa está dividido em subprogramas, a fim de facilitar a compreensão.
A rotina em linguagem de máquina efetua a leitura do header do disco ou da fita e está presente no programa através das linhas DATA.
Este programa, devido à sua função, só opera em sistemas que possuam unidade de disco.
Arquivo: tap2dsk.bas
Listagem Principal:
0 ' Marcello Fontolan
1 ' Itajai, 25 de janeiro de 1988
2 ' HYPER COPY
3 ' Copiador disco-fita-Disco V1.0
10' Limpa tela
20 CLS:KEYOFF:SCREEN0:COLOR15,1,1
30 ' Define teclas de funcao
40 FOR F=1 TO 3: KEY F,"": NEXT F
50 FOR F=6 TO 10: KEY F,"": NEXT F
60 KEY 4,"RUN"+CHR$(34)+"HYPER"+CHR$(34)+CHR$(13)
70 KEY 5,"RUN"+CHR$(13)
80 ' Apresentacao do menu principal
90 PRINT " -------------------------------------- ": LOCATE 0,1: PRINT "! !": LOCATE 0,2 : PRINT ".--------------------------------------."
100 LOCATE 1,1 : PRINT"------------ HYPER COPY ---- V1.0 ----": PRINT: PRINT : PRINT
110 PRINT: PRINT TAB(10) "Digite:"
120 ' Opcoes do Menu
130 PRINT: PRINT TAB(5) "1 --> Copia de Disco p/ Fita"
140 PRINT: PRINT TAB(5) "2 --> Copia de Fita p/ Disco"
150 PRINT: PRINT TAB(5) "B --> Sair"
160 A$=INKEY$: IF A$="" THEN 160
170 ' Analisa opcao desejada
180 IF A$="B" OR A$="b" THEN END
190 IF A$="1" THEN 230
200 IF A$="2" THEN 880
210 GOTO 160
220 ' Copia de Disco p/ Fita
230 CLS
240 PRINT "-------- Copia de Disco p/ Fita --------"
250 ' Apresentacao do diretorio
260 PRINT: FILES
270 ' Espera nome do arquivo
280 PRINT: PRINT : LINE INPUT "Digite o nome do arquivo: ";N$
290 ' Desvia programa caso tenha erro
300 ON ERROR GOTO 670
310 ' Abre arquivo
320 OPEN N$ FOR INPUT AS#1
330 ' Obtem dados sobre o arquivo
340 F=ASC(INPUT$(1,#1))
350 IF F<>254 THEN 790
360 X$=INPUT$(6,#1)
370 ' Fecha o arquivo
380 CLOSE#1
390 ' Calcula parametros
400 CLS
410 PI=(ASC(MID$(X$,2,1)))*256+(ASC(MID$(X$,1,1)))
420 PF=(ASC(MID$(X$,4,1)))*256+(ASC(MID$(X$,3,1)))
430 PE=(ASC(MID$(X$,6,1)))*256+(ASC(MID$(X$,5,1)))
440 TM=(PF-PI)+1
450 ' Transforma dados decimais em Hex.
460 TM$=RIGHT$("0000"+HEX$(TM),4)
470 PI$=HEX$(PI):PF$=HEX$(PF):PE$=HEX$(PE)
480 ' Apresenta dados sobre o arquivo
490 PRINT "Arquivo :";N$
500 PRINT "Formato :";: IF F=254 THEN PRINT"ASSEMBLER"
510 PRINT "Endereco Inicial : &H";PI$
520 PRINT "Endereco Final : &H";PF$
530 PRINT "Ponto de Execucao : &H";PE$
540 PRINT "Tamanho : &H";TM$
550 ' Apresenta comando de leitura
560 PRINT: PRINT: PRINT "bload";CHR$(34);N$;CHR$(34)
570 ' Apresenta comando de gravacao
580 PRINT: PRINT "bsave";CHR$(34);"CAS:";LEFT$(N$,6);CHR$(34);",&H";PI$;",&H";PF$;",&H";PE$
590 PRINT:PRINT
600 ' Apresetna tecla a ser pressionada
610 IF PI<37000! THEN PRINT "Tecle 'F4'." ELSE PRINT "Tecle 'F5'."
620 ' Coloca o cursor em posicao
630 LOCATE 0,7
640 ' Termina a execucao
650 END
660 ' Tratamento de erros
670 CLOSE#1
680 CLS
690 PRINT"--------- Tratamento de Erros ----------"
700 PRINT: PRINT
710 IF ERR=62 THEN PRINT " O Drive especificado nao existe !"
720 IF ERR=56 THEN PRINT " O Nome do arquivo esta incorreto !"
730 IF ERR=60 THEN PRINT " O Disco esta com problema de formato !"
740 IF ERR=53 THEN PRINT " O Arquivo nao existe !"
750 PRINT: PRINT " Tecle algo para continuar ..."
760 A$=INKEY$: IF A$="" THEN 760
770 RESUME 20
780 ' Tratamento de arquivos nao BSACE
790 CLOSE#1
800 CLS
810 PRINT"--------- Tratamento de Erros ----------"
820 PRINT: PRINT
830 PRINT " O arquivo especificado nao se encontra gravado com o BSAVE do MSX DISK BASIC!"
840 PRINT: PRINT " Tecle algo para continuar ..."
850 A$=INKEY$: IF A$="" THEN 850
860 GOTO 20
870 ' Copia de Fita p/ Disco
880 RESTORE 890
890 DATA cd,e1,00,d8,21,00,c0,06,10,e5,c5,cd,e4,00,c1,e1,d8,77,23,10,f4,cd,e1,00,d8,21,10,c0,06,06,e5,c5,cd,e4,00,c1,e1,d8,77,23,10,f4,c9
900 ' Armazena dados em ASSEMBLER
910 FOR P=0 TO 42
920 READ A$
930 POKE(&HE050)+P,VAL("&H"+A$)
940 NEXT P
950 DEFUSR=&HE050
960 DEFFNPE(X)=PEEK(X)+256*PEEK(X+1)
970 ' Apresentacao
980 CLS
990 PRINT "-------- Copia de Fita p/ Disco --------"
1000 PRINT : PRINT "---------- Prepare o Gravador ----------"
1010 PRINT : PRINT "---- E tecle algo quando pronto ... ----"
1020 A$=INKEY$: IF A$="" THEN 1020
1030 ' Leitura de parametros
1040 X=USR(0): MOTOR OFF
1050 ' Calculo do nome
1060 FOR R=(&HC00A) TO (&HC00F)
1070 P$=CHR$(PEEK(R))
1080 N$=N$+P$
1090 NEXT R
1100 ' Obtem formato
1110 F=PEEK(&HC000)
1120 IF F<>208 THEN 20
1130 ' Calcula parametros
1140 PI=FNPE(&HC010):'Ponto inicial
1150 PF=FNPE(&HC012):'Ponto final
1160 PE=FNPE(&HC014):'Ponto execucao
1170 TM=(PF-PI)+1
1180 ' Transforma dados decimais em HEX
1190 PI$=HEX$(PI):PF$=HEX$(PF):PE$=HEX$(PE):TM$=RIGHT$("0000"+HEX$(TM),4)
1200 ' Apresenta dados sobre o arquivo
1210 CLS
1220 PRINT "Arquivo : ";N$
1230 PRINT "Formato : ASSEMBLER"
1240 PRINT "Endereco Inicial : &H";PI$
1250 PRINT "Endereco Final : &H";PF$
1260 PRINT "Ponto de Execucao : &H";PE$
1270 PRINT "Tamanho : &H";TM$
1280 ' Apresenta comando de leitura
1290 PRINT : PRINT : PRINT "bload";CHR$(34);"CAS:";N$;CHR$(34)
1300 ' Apresenta comando de gravacao
1310 PRINT : PRINT : PRINT "bsave";CHR$(34);N$;CHR$(34);",&H";PI$;",&H";PF$;",&H";PE$
1320 PRINT:PRINT
1330 ' Apresenta tecla a ser pressionada
1340 IF PI<37000! THEN PRINT "Tecle 'F4'." ELSE PRINT "Tecle 'F5'."
1350 ' Coloca cursor em posicao
1360 LOCATE 0,7
1370 ' Termina a execucao
1380 END
Listagem da Rotina em Assembly:
TAPION equ #e1 ; Tape Input on
TAPIN equ #e4 ; Tape Input
org #e050 ; Define Endereco inicial
E050 CD E1 00 call TAPION ; Prepara o gravador para leitura de dados
E053 D8 ret c ; TAPION/TAPIN setam o registro C em caso de falha,
E054 21 00 C0 ld hl,#c000 ; Ponto onde serão armazenados os valores do tipo de arquivo
E057 06 10 ld b,#10 ; que e composto por 10 bytes no inicio do header
E059 E5 LOPI1: push hl ; Salva registros e inicia um loop para ler 10 bytes
E05A C5 push bc
E05B CD E4 00 call TAPIN ; Le um byte via K7 e armazena em A
E05E C1 pop bc ; Recupera registros
E05F E1 pop hl
E060 D8 ret c ; Retorna em caso de erro
E061 77 ld (hl),a ; Armazena o valor de A no endereco de destino do header
E062 23 inc hl ; incrementando
E063 10 F4 djnz LOPI1 ; e prosseguindo o looping
E065 CD E1 00 call TAPION
E068 D8 ret c
E069 21 10 C0 ld hl,#c010 ;Onde serão armazenadas informacoes de enderecamento do arquivo
E06C 06 06 ld b, #06 ; que estão nos proximos 6 bytes do header
E06E E5 LOPI2: push hl ; Loop para recuperacao dos mesmos
E06F C5 push bc
E070 CD E4 00 call TAPIN
E073 C1 pop bc
E074 E1 pop hl
E075 D8 ret c
E076 77 ld (hl),a
E077 23 inc hl
E078 10 F4 djnz LOPI2
E07A C9 ret ; Fim da rotina, retorno ao BASIC
Gerando Sons no MSX
Artigo Original: Antônio F.S.Shalders, Revisão: Wilson Pilon
O seu MSX possui um processador de áudio modelo AY-3-8910, produzido pela General Instruments (EUA). Este integrado é do tipo LSI (Large Scale Integration) e, embora não seja o processador de áudio mais sofisticado do mercado, é um dos mais facilmente encontráveis, além de ser de fácil implementação e operação.
O BASIC possui uma instrução que permite trabalharmos diretamente com os seus 14 registradores, o SOUND, cuja sintaxe é SOUND REGISTRADOR, VALOR.
Além do SOUND, existe a macro-linguagem PLAY, que nos permite fazer bons trabalhos, desde simples vinhetas até acordes.
O processador de áudio, ou simplesmente PSG (Programmable Sound Generator), possui 3 canais, com 8 oitavas cada, num total de 96 notas musicais disponíveis, além dos geradores de ruído e envoltória.
O PSG também controla a leitura de dados do gravador e as portas multiuso de oito bits, que são as entradas para joystick.
Como Gerar um Tom
Para gerarmos um tom de frequência pré-determinada em um canal de áudio, devemos carregar nos registradores relativos aos ajustes de frequência fino e grosso do canal em questão, selecionar a envoltória desejada, o volume de saída e a atuação ou não do misturador de canais.
O maior mistério para muitos é como calcular os valores necessários à carga dos registradores de ajuste de frequência, o que é, na realidade, muito simples, pois basta aplicarmos a seguinte fórmula, a fim de obtermos o valor principal:
Cálculo de Frequência:
onde C é o clock do seu computador, em hertz e F é a frequência desejada.
Os clocks do Hotbit e do Expert são 3579545 Hz e 3575611 Hz, respectivamente, ou seja: aproximadamente 3.58 MHz
Os valores que devem ser atribuídos aos registradores de ajuste fino (AF) e grosso (AG) podem ser facilmente obtidos pelas expressões abaixo:
e
É bom notar que a resolução das frequências obtidas é inversamente proporcional à frequência de maneira exponencial, pois, à medida que aumentamos a frequência, a resolução diminui.
Na faixa dos 100Hz, por exemplo, a resolução chega a ser melhor que 1Hz. Já na faixa dos 5Khz, a resolução já cai para certa de 400Hz, logo não é muito lógico programarmos o PSG para tons alternados de 5000Hz e 5010Hz, por exemplo, pois os dois tons gerados seriam idênticos.
A menor frequência que pode ser gerada é de 4Hz e a máxima vai além de 60Hz, o que não adianta muito, pois a faixa audível vai somente até cerca de 16Khz, se seu ouvido for bom!
A tabela 1 exibe os registradores do PSG e suas funções:
Registro | Função |
0 | AF canal A |
1 | AG canal A |
2 | AF canal B |
3 | AG canal B |
4 | AF canal C |
5 | AG canal C |
6 | Ajuste da freq.centr.do ruído |
7 | Controle do misturador |
8 | Volume do canal A |
9 | Volume do canal B |
10 | Volume do canal C |
11 | AF da freq.da envolt. |
12 | AG da freq.da envolt. |
13 | Tipo da envoltória |
Mais sobre o PSG
O PSG possui um gerador de ruído branco que pode ser usado em conjunto ou não com os três canais analógicos.
O chamado ruído branco é caracterizado por uma mistura aleatória de frequências de amplitudes iguais ou não. É exatamente o chiado que aparece em um aparelho de televisão quando sintonizamos um canal livre. Efeitos muitíssimo interessantes podem ser conseguidos com o uso racional deste recurso do AY-3-8910.
Um ponto forte do nosso processador de áudio é, sem dúvida alguma, a capacidade de controle da envoltória de um som (ou envelope, como dizem alguns) é a forma com que a intensidade ou amplitude varia em função do tempo.
É graças à diferentes formas de envoltórias que podemos distinguir o som de uma guitarra do de uma flauta, mesmo que ambos sejam exatamente da mesma frequência.
É claro que existem infinitos tipos de envoltórias, sendo estas responsáveis pelo timbre e nuances dos sons. A envoltória pode ser representada desde uma função constante até funções ultra complexas.
O nosso PSG não pode simular todos esses tipos de envoltórias, mas pode simular qualquer combinação de envoltórias da família "dente de serra", que inclui a triangular e a constante, gerando efeitos que chegam a ser estonteantes.
O som das ondas do mar, por exemplo, é caracterizado por um ruído branco, cuja envoltória é da família senoidal (na realidade, é uma sobreposição de várias senóides aleatórias), mas podemos obter ótimos resultados com a envoltória triangular!
Já o som de uma explosão ou de um sino tem como envoltória uma função exponencial inversa, do tipo Y=1/X, mas podemos obter resultados muito próximos disso com uma envoltória do tipo dente de serra, com a rampa negativa, o que caracteriza um início abrupto, seguido de um decaimento suave do som.
O registrador responsável por isso é o 13 e podemos escolher 8 tipos de envoltórias de família dente de serra e constante, ou algumas combinações destes.
A tabela 2 mostra os tipos de envoltórias com as quais podemos programar o PSG.
O método usado para tal é semelhante ao do ajuste dos canais analógicos de saída. Para o ajuste da frequências dominante do ruído branco, não aconselho a usar fórmulas de espécie alguma, pois é melhor escolhermos "de ouvido" o que mais nos agradar. Os valores possíveis para carregarmos o registrador 6 variam de 0 a 31, sendo que, quanto maior este valor, mais grave é o tom dominante.
Já para o ajuste de frequência da envoltória, necessitamos de uma fórmula do mesmo tipo da que mostramos para o ajuste dos canais analógicos de saída:
Com esta fórmula, podemos obter frequências desde 0.1 Hz!
Os valores a serem armazenados nos registradores de ajuste fino e grosso são achados da mesma maneira descrita anteriormente para os canais analógicos.
É importante notar que o gerador de envoltórias está ativo somente quando o volume do canal está no máximo.
O Controle Misturador
O objetivo deste controle é combinar, selecionar, ativar ou desativar os canais de som e os geradores de envoltória e ruído. Esta seleção é feita através do valor atribuído ao registrador 7 do PSG.
Cada bit deste valor tem uma função específica e, para a parte sonora, são usados apenas o bits de 0 a 5. Os bits 6 e 7 são usados na verificação dos estados das portas multiuso.
Convém informar-lhes que os bits relativos a este registro são ativos em zero, e suas funções são mostradas na tabela 3.
Bit | Função quando em 0 |
0 | Habilita o canal A |
1 | Habilita o canal B |
2 | Habilita o canal C |
3 | Habilita ruído no canal A |
4 | Habilita ruído no canal B |
5 | Habilita ruído no canal C |
Se desejarmos habilitar os canais A e B com som puro (sem ruído) e o canal C com ruído, devemos configurar o registrador 7 com o valor &B00011100, ou seja: SOUND 7,56. Um canal pode ser usado para gerar tom e ruído simultaneamente, mas não podemos ter mais de duas envoltórias diferentes nos três canais. No caso de termos duas, uma terá que ser, obrigatoriamente, uma função constante, ou seja: o volume do canal em questão deverá ser menor que 16.
Existem diversos editores musicais e sonoros para o MSX e alguns de altíssimo desempenho, como por exemplo, o SOUND (p/disco) e o SUPER SYNTH (na minha opinião, o melhor). Se você não possuir nenhum destes programas, não se desespere, pois o programa da listagem 1, se usado com bom senso, pode trazer resultados muito bons na elaboração de sons complexos.
Exemplo Prático do programa
Como já temos o editor de sons, falta somente entendermos como os sons são compostos, ou seja: A combinação adequada de frequências, volumes, ruído e envoltória, afim de obtermos o som desejado, como por exemplo, o som do mar.
Este som é caracterizado por ataque e dacaimento suaves, modulando um ruído de média frequência (ondas quebrando). A envoltória que nos proporciona este efeito é a de número 14.
Para ativarmos esta envoltória, é necessário que o volume do canal em questão esteja ajustado em 16. Feito isso devemos ajustar a frequência do ruído, que no caso é cerca de 25. Devemos agora, ajustar os controles da envoltória e seu respectivo preriodo (14 e .2), e selecionar o ruído ativo no canal "A".
Exemplo:
CANAL A: 0,16
CANAL B E C: 0,0
FREQUÊNCIA DO RUÍDO: 25
TIPO DE ENVOLTÓRIA: 14
FREQUÊNCIA DA ENVOLTÓRIA: .2
TOM ATIVO NOS CANAIS : N
RUÍDO ATIVO EM "A": S
RUÍDO ATIVO EM "B" E "C": N
Listagem:
10 REM
20 REM REVISTA CPU - MAIO 1988
30 REM ANTONIO FERNANDO SHALDERS
40 REM
50 REM GERANDO SONS NO MSX
100 KEYOFF:SCREEN0:COLOR15,1:CLEAR
110 PRINT "HOTBIT ou EXPERT (H/E)":PRINT
120 A$=INKEY$:IF A$="H" OR A$="h" THEN C=3579545# ELSE IF A$="E" OR A$="e" THEN C=3575611# ELSE 120
125 PRINT"* FREQUENCIA (4 a 16000)","* VOLUME (0 a 16) (16 liga a envoltoria)":PRINT
130 PRINT "CANAL A:":INPUT "FREQUENCIA,VOLUME ";FA,VA:PRINT
140 IF FA=0 THEN FA=4
150 PRINT "CANAL B:":INPUT "FREQUENCIA,VOLUME ";FB,VB:PRINT
160 IF FB=0 THEN FB=4
170 PRINT "CANAL C:":INPUT "FREQUENCIA,VOLUME ";FC,VC:PRINT
180 IF FC=0 THEN FC=4
190 NA=INT(C/FA/32):NB=INT(C/FB/32):NC=INT(C/FC/32)
200 FA=NA MOD 256:GA=NA \ 256
210 FB=NB MOD 256:GB=NB \ 256
220 FC=NC MOD 256:GC=NC \ 256
230 INPUT "FREQ. DOMINANTE DO RUIDO (0-31) ";FR:PRINT
240 INPUT "TIPO DE ENVOLTORIA ";TE:PRINT
250 INPUT "FREQ. da ENVOLTORIA (MIN=.1) ";WE:PRINT:IF WE=0 THEN WE=.1
260 NE=C/(1100*WE):FE=NE MOD 256:GE=NE \ 256
270 PRINT"TOM ATIVO EM A(S/N) ";
280 GOSUB 690:IF A$="S" THEN TA=0 ELSE IF A$="N" THEN TA=1
281 PRINT A$:PRINT
290 PRINT"TOM ATIVO EM B(S/N) ";
300 GOSUB 690:IF A$="S" THEN TB=0 ELSE IF A$="N" THEN TB=2
301 PRINT A$:PRINT
310 PRINT"TOM ATIVO EM C(S/N) ";
320 GOSUB 690:IF A$="S" THEN TC=0 ELSE IF A$="N" THEN TC=4
321 PRINT A$:PRINT
330 PRINT"RUIDO ATIVO EM A(S/N) ";
340 GOSUB 690:IF A$="A" THEN RA=0 ELSE IF A$="N" THEN RA=8
341 PRINT A$:PRINT
350 PRINT"RUIDO ATIVO EM B(S/N) ";
360 GOSUB 690:IF A$="A" THEN RB=0 ELSE IF A$="N" THEN RB=16
361 PRINT A$:PRINT
370 PRINT"RUIDO ATIVO EM C(S/N) ";
380 GOSUB 690:IF A$="A" THEN RC=0 ELSE IF A$="N" THEN RC=32
381 PRINT A$:PRINT
390 M=0:M=TA+TB+TC+RA+RB+RC:CLS
400 SOUND 0,FA:PRINT"SOUND 0,";FA
410 SOUND 1,GA:PRINT"SOUND 1,";GA
420 SOUND 2,FB:PRINT"SOUND 2,";FB
430 SOUND 3,GB:PRINT"SOUND 3,";GB
440 SOUND 4,FC:PRINT"SOUND 4,";FC
450 SOUND 5,GC:PRINT"SOUND 5,";GC
460 SOUND 6,FR:PRINT"SOUND 6,";FR
470 SOUND 7,M:PRINT"SOUND 7,";M
480 SOUND 8,VA:PRINT"SOUND 8,";VA
490 SOUND 9,VB:PRINT"SOUND 9,";VB
500 SOUND 10,VC:PRINT"SOUND 10,";VC
510 SOUND 11,FE:PRINT"SOUND 11,";FE
520 SOUND 12,GE:PRINT"SOUND 12,";GE
530 SOUND 13,TE:PRINT"SOUND 13,";TE
680 PRINTSTRING$(13,95):END
690 A$=INKEY$:IF A$<>"S"ANDA$<>"N"ANDA$<>"s"ANDA$<>"n" THEN 690
691 IF A$="S"OR A$="N" THEN RETURN
700 IF A$="s" THEN A$="S":RETURN
710 IF A$="n" THEN A$="N":RETURN
Arquivo: esound.bas
Slots e Expansões
Artigo Original: Andre L.F.de Freitas, Revisão: Wilson Pilon
Você já deve ter se indagado a respeito do sistema de SLOTS de seu MSX: como funcionam os cartuchos de jogos, interfaces, ou qualquer periférico conectado ao seu MSX via cartuchos. Neste artigo, procurarei esclarecer as dúvidas a respeito dos slots e páginas de memória que são um pequeno enigma na vida de usuários de micros padrão MSX.
O microprocessador Z80 é um processador de 8 bits capaz de endereçar 65536 bytes de memória (64 KBytes). De que forma, então, podemos ter 32 Kbytes de memória ROM e mais 64 KBytes de memória RAM para uso do micro? Como podemos ter, no exterior, micros do padrão MSX com expansões de memória de 128 KBytes? O responsável por isso é um circuito integrado chamado PPI, do inglês Peripheral Programable Interface, de identificação 8255. A descrição mais detalhada deste "chip", termo que usarei daqui em diante, ficará para uma outra ocasião, pois, no momento, só nos interessa o trabalho que ele realiza no micro.
Este chip é uma interface paralela contendo 4 portas de 8 bits cada. Uma destas portas, a qual chamaremos de porta A, é responsável pela lógica de seleção de slots do MSX. Cada grupo de dois bits desta porta pode conter um número entre 0 e 3, o qual vai indicar em que slot do MSX a página de memória correspondente vai estar ativa. Para entender melhor, observe a figura 1.
Como exemplo, se quisermos uma configuração semelhante a da figura 2, teremos o seguinte valor na porta A da PPI: &B00001010. Dividindo este número em quatro blocos de 2 bits, teremos: &B00, &B00, &B10, &B10. Em decimal, teremos 0, 0, 2, 2, que, na ordem da primeira página até a quarta, significa que as duas primeiras estão ativas no slot 0 e as duas superiores no slot 2. Esta é uma das configurações de memória mais usada no MSX e seu micro pode, inclusive, ter esta configuração.
Cada página também pode ter uma expansão para mais 3 páginas iguais sobrepostas. Mas esta seleção não é mais tão simples como a primeira. Portanto, o seu micro poderia ter mais 3 blocos de memória de 256 Kbytes sobrepostos ao primeiro, o que daria um total de 1 Mbyte de memória. Ótimo, não é mesmo?
Aí, surge o problema de endereçamento de 64 Kbytes. Apesar de toda esta memória, linearmente, só podemos ter 64 Kbytes ativos.
Provavelmente, você deve estar odiando o Z80, mas não fique chateado. Você pode chavear páginas de memória mudando o valor da porta A da PPI, podendo acessar todas as outras páginas quando quiser. Mas lembre-se: nunca mais de 64 Kbytes simultaneamente.
Quando o seu MSX é ligado, os slots são pesquisados à procura de memória RAM. Este teste é feito das mais altas posições de memória para baixo. A medida que o sistema encontra RAM, vai habilitando esta memória para uso. Se houver mais de duas páginas contendo memória RAM entre os endereços &H8000 e &HFFFF, ele habilitará as páginas mais próximas ao slot 0. O seu MSX fica, então com as páginas zero e um no slot zero contendo todas as rotinas internas de operação e o interpretador BASIC, que estão em ROM, nestas páginas, e a memória RAM livre nas páginas 2 e 3 em algum outro slot. A partir daí, ele executa uma inicialização em variáveis de sistema e outras funções prioritárias para, depois, entrar no interpretador BASIC.
Para saber qual a configuração de memória do seu MSX, entre com o pequeno programa em BASIC da listagem e rode-o. Ele mostratá quais as páginas ativas do seu micro, pois a configuração pode variar conforme o fabricante. O programa faz uma leitura na porta &HA8 do micro, a qual endereça a porta A da PPI. O valor correspondente lido é, então, dividido em grupos de 2 bits e passado para decimal, informando em que slot estão as páginas de memórias do micro.
Agora qque já sabemos como funciona a paginação de memória dos microcomputadores MSX, podemos conhecer um pouco mais do sistema de expansões.
Quem possui uma interface de disco, cartão de 80 colunas, ou mesmo um cartucho de jogos, já verificou que o mesmo é conectado a uma das entradas de cartucho do micro. Cada entrada destas contém um barramento e uma lógica de seleção correspondente a um dos slots do MSX. Quando um destes slots contém um periférico ou um cartucho de jogo, a inicialização do sistema procura um certo conjunto de bytes na memória correspondente a estas entradas. Estes bytes contêm informações úteis do sistema para que este reconheça o tipo de periférico conectado. Estas informações podem indicar se existe ROM no slot, o seu endereço de execução, se existe expansão de comando a serem utilizados pela instrução CALL do basic, endereço de rotinas para manipulação de dispositivos, etc.
Como conclusão desta parte do artigo, vemos que o sistema de slots do MSX é algo de grande valor, pois, através dele, podemos manipular toda a memória e acessar um grande número de prefiféricos já existentes para a linha. Futuramente, em uma outra oportunidade, iremos explorar mais o acesso a periféricos. Trataremos, agora, somente da memória de nosso microcomputador.
A seguir, veremos um programa que permitirá a vocês, usuários, explorar toda a memória "adormecida" do MSX.
Vamos supor que temos um programa em linguagem de máquina que não necessita do interpretador BASIC para ser executado. É o caso dos jogos em linguagem de máquina que você, provavelmente, possui. Vamos supor que o programa tem mais de 32 Kbytes de comprimento. Ora, se o micro só tem ativos 32 Kbytes de RAM, como podemos carregar e executar este programa? Simples. O programa é, geralmente dividido em blocos menores com 16 Kbytes cada. Ao se ler o primeiro bloco com a instrução BLOAD, este bloco é carregado em uma página ativa do sistema e seu ponto de entrada é uma pequena rotina que verifica se há uma página de RAM correspondente à página do interpretador BASIC (página 1) em ouro slot diferente do 0. Ao encontrá-lo, muda o valor da porta A da PPI para se configurar possuindo a página 1 em RAM. A seguir o programa é realocado para esta página e a porta A da PPI recebe, novamente, a configuração original do sistema, retornando o controle do micro ao interpretador BASIC. Pronto. Temos 16 Kbytes de RAM contendo um programa em uma região da memória que não estava sendo utilizada; uma daquelas páginas que, a princípio, parecia não ter utilidade. Podemos fazer isto com vários blocos de 16 Kbytes de programa, desde que tenhamos páginas de memória suficientes para contê-las.
Outra limitação é a de nunca desativarmos a página 0 da ROM, pois lá estão contidas as rotinas básicas de operação do MSX. Se ativarmos a página 0 em outro slot, o programa lá contido deve realizar algumas destas funções, sob pena de perdermos o controle do micro. O mesmo é válido para a página 3, pois lá se encontram variáveis do sistema muito importantes para o perfeito funcionamento do micro. Ao final do carregamento dos blocos, o último deve conter uma rotina que manipule as páginas ocultas nos slots "vazios" do micro, e teremos então, um belo jogo ou utilitário com mais de 32 Kbytes de comprimento. Para se rodar o programa, basta configurar a PPI para ativar a página correspondente ao bloco que queremos executar e pular para um endereço naquele bloco. Pode ser um pouco estranho ficar pulando de um lugar para outro, mas o resultado, quase sempre, é um ótimo jogo ou utilitário, o qual já fez com que muitos de nós perdêssemos algumas horas utilizando-o com grande prazer.
O programa da listagem 2 é uma pequena rotina em linguagem de máquina que procura uma página de RAM correspondente aos endereços &H4000 até &H7FFF nos slots livres do seu MSX e realoca para lá um programa em assembly qualquer. Como exemplo de programa a ser realocado, temos o fornecido na listagem 3. Este programa somente imprime uma mensagem no vídeo sem a necessidade do interpretador BASIC. Na listagem 4 temos uma outra rotina que só chamará o programa de impressão na página em que se encontrar e retornará ao interpretador BASIC.
Observando a listagem 2, veremos que a rotina começa tentando escrever um valor na memória e, depois, tenta ler de volta o mesmo valor. Este teste tem de ser feito duas vezes com valores diferentes, pois este endereço poderia ser em ROM e conter o valor testado, o que não nos levaria a nenhuma conclusão. Se for encontrada RAM, o programa passa a realocar o programa da listagem 3. Portanto, antes de rodar o programa da listagem 2, certifique-se de já ter digitado, também, o da listagem 3. Se o slot testado não puder ser usado, o programa continuará a procurar até encontrar uma área livre.
Você deverá utilizar um monitor assembler para entrar os programas na memória, mas, caso não possua um, não se desespere. Na listagem maior (5) eu forneço um programa em BASIC que colocará na memória, automaticamente, os programas 2, 3 e 4 que estão em instruções DATA nas linhas iniciais do programa.
Após os três programas estarem na memória, estamos prontos para rodá-los. Digite, no BASIC, as linhas abaixo
DEF USR=&HE000
A=USR(0)
Pronto.
Agora, o programa de impressão já está em uma página de memória livre do seu MSX. Se você já digitou a listagem 4, estamos prontos para testar a paginação de memória do MSX. Repare que o programa da listagem 4 lê um byte do endereço &HE100 que contém a configuração da porta A da PPI para a página 1 ser em memória RAM. Esta configuração foi escrita ali pelo programa 2 para sabermos, exatamente, onde é esta RAM e não causarmos uma perda de controle do sistema. Digite, agora, no Basic, as seguintes linhas abaixo:
DEF USR1=&HE200
A=USR1(0)
Aí está a sua mensagem, sem que o interpretador BASIC estivesse ativo durante sua execução. O programa da listagem 4 alterou a configuração das páginas de memória, desativando o interpretador BASIC, chamou a rotina de impressão a qual usa uma chamada a uma rotina do BIOS para imprimir, escrevendo direto através do processador de vídeo e restaurou o sistema para que tivéssemos, novamente acesso ao BASIC.
Gostaram?
Espero que vocês não fiquem só por aí. As listagens são apenas pequenso exemplos do que se pode fazer com a paginação de memória do MSX. Deixo, aqui, a sugestão para que vocês alterem à vontade estes programas e se utilizem destas rotinas quando precisarem de "mais memória". Criem à vontade, pois, como vocês podem ver, o MSX é um micro novo no mundo e mais novo ainda aqui no Brasil, havendo, ainda muita coisa a ser explorada.
Estarei à disposição de vocês para sugestões e opiniões, bastando entrar em contato com a diretoria técnica da revista.
Espero que todos tenham gostado deste primeiro artigo e, como já disse anteriormente, não ficaremos por aqui. Ainda há muito a se explorar. Aguardem.
Arquivos: slot-1b.bas, slot-2b.bas, slot-2.asm, slot-3.asm, slot-4.asm
Listagem 0:
20 REM SLOTS E EXPANSOES
30 REM AUTOR: ANDRE LUIZ FREITAS
40 CLS
50 A = INP ( &HA8 )
60 A$ = BIN$ ( A )
70 FOR I=1 TO 8 STEP 2
80 B$ = MID$ (A$,I,2)
90 J = 3 - INT ( I/2 )
100 PRINT "PAGINA:"; J; " SLOT: "; VAL ( "&B" + B$ )
110 NEXT I
Listagem 1:
110 REM SLOTS E EXPANSOES
120 REM AUTOR: ANDRE FREITAS
130 REM
140 REM ESTE PROGRAMA BASIC COLOCA NA
150 REM MEMORIA AS ROTINAS EM LINGUAGEM
160 REM DE MAQUINA CONTIDAS NAS LINHAS
170 REM DATA.
180 REM ESTAS ROTINAS CORRESPONDEM
190 REM AS LISTAGENS 2,3,4 DESTE ARTIGO
200 REM
210 REM
220 REM PRIMEIRA ROTINA - LISTAGEM 2
230 DATA db,a8,32,f0,e0,21,00,40
240 DATA 06,03,db,a8,c6,04,32,f1
250 DATA e0,d3,a8,3e,aa,77,56,2f
260 DATA 77,5e,7a,83,fe,ff,28,04
270 DATA 10,e8,18,0b,21,00,e1,11
280 DATA 00,40,01,30,00,ed,b0,3a
290 DATA f0,e0,d3,a8,c9
300 REM SEGUNDA ROTINA - LISTAGEM 3
310 DATA 21,0f,40,7e,fe,00,28,06
320 DATA cd,a2,00,23,18,f5,c9,0c
330 DATA 53,4c,4f,54,53,20,45,20
340 DATA 45,58,50,41,4e,53,4f,45
350 DATA 53,07,00
360 REM TERCEIRA ROTINA - LISTAGEM 4
370 DATA 3a,f1,e0,d3,a8,cd,00,40
380 DATA 3a,f0,e0,d3,a8,c9
390 REM
400 FOR I=&HE000 TO &HE034
410 READ A$:A=VAL("&H"+A$):POKE I,A
420 NEXT I
430 FOR I=&HE100 TO &HE122
440 READ A$:A=VAL("&H"+A$):POKE I,A
450 NEXT I
460 FOR I=&HE200 TO &HE20D
470 READ A$:A=VAL("&H"+A$):POKE I,A
480 NEXT I
490 CLS:PRINT"ROTINAS CARREGADAS NA MEMORIA"
Listagem 2:
; SLOTS E EXPANSOES
; AUTOR: ANDRE FREITAS
; LISTAGEM 2
org #E000
in a,(#a8) ;Le configuracao da porta A da PPI
ld (#e0f0),a ;Salva no endereco &HE0F0
ld hl,#4000 ;Endereco na pagina 1
ld b,3 ;No. de SLOTS a testar
Loop: in a,(#a8) ;Le PPI
add a,4 ;Incrementa bits correspondentes a pagina 1 da PPI
ld (#e01f),a ;Salva no endereco &HE0F1
out (#a8),a ;Altera a PPI
ld a,#aa ;Valor a ser escrito na memoria equivale a &B10101010
ld (hl),a ;Escreve e
ld d,(hl) ;le da memoria
cpl ;Complementa este valor. Se a primeira leitura for correta, o complemento sera &H01010101
ld (hl),a ;Escreve e
ld e,(HL) ;le na memoria
ld a,d
add a,e ;Soma os dois valores lidos ma memoria
cp #ff ;Se ha RAM no SLOT, o valor da soma e &HFF e o teste esta correto
jr z,Achou ;entao salta para a rotina de transferencia de blocos, senao
djnz Loop ;decrementa 8 e volta ao loop de teste
jr Fim ;Nao achando RAM ao fim do loop, sai
Achou: ld hl,#e100 ;Inicio do bloco a transferir
ld de,#4000 ;Endereco de Destino
ld bc,#30 ;Tamanho do bloco
ldir ;transfere
Fim: ld a,(#e0f0) ;Carrega A com a configuracao original da PPI
out (#a8),a ;Restaura valor na porta A da PPI
ret
;Ao final deste programa teremos:
;
;No Endereco &HE0F0 - A configuracao original daporta A da PPI
;No Endereco &HE0F1 - A confihuracao para a qual a pagina 1 esta em RAM
Listagem 3:
; Slots e expansoes
; Autor: Andre Freitas
; Listagem 3
org #E100
chput: equ #A2 ;Rotina de impressao do bios
ld hl,#400F ;Carrega HL com endereco da mensagem (end. apos realocar)
loop: ld a,(hl) ;Carrega A com valor de caracter
cp 0 ;Compara com 0
jr z,fim ;Se for zero encerra o programa
call chput ;Chama rotina de impressao do bios
inc hl ;Avanca caracter na mensagem
jr loop ;Volta para o laco de impressao
fim: ret
mensg: defb #0c
defm 'SLOTS E EXPANSOES'
defb 7
defb 0
Listagem 4:
; slots e expansoes
; autor: Andre Freitas
; listagem 4
org #E200
ld a,(#e0f1) ;Carrega acumulador com a configuracao de PPI salva pelo programa da listagem 2
out (#A8),a ;Altera a configuracao da PPI
call #4000 ;Chama programa de impressao na pagina 1
ld a,(#E0F0) ;Carrega acumulador com a configuracao normal da PPI salva pelo programa da listagem 2
out (#A8),a ;Altera configuracao da PPI
ret
O Sistema de gravação cassete no MSX
Artigo orignal: André L.F.de Freitas, Digitação/Correção: Wilson Pilon
Todos os usuários de micros padrão MSX já estão familiarizados com o seu sistema de gravação. Estes micros se utilizam de um processo de gravação chamado FSK, do inglês Frequency Shift Keying, ou seja, chaveamento de frequência. Aquele som que ouvimos quando acionamos o gravador com uma fita cassete contendo um programa é uma sequência de pulsos em frequências diferentes correspondentes a bits zeros e uns. Estas frequências são entre 1200 hertz e 4800 hertz, dependendo da velocidade de gravação. Podem ser utilizadas outras frequências e, de certa forma, isto é fácil de se conseguir, alterando algumas variáveis do sistema MSX. Mas não é aconselhável, pois os circuitos de áudio do MSX possuem filtros que eliminam frequências superiores, o que reduz a confiabilidade da gravação.
O que a maioria dos usuários não sabe é o que está por trás de uma simples instrução SAVE e como isto é processado pelo micro a nível de máquina. Pretendo, neste artigo, dar uma pequena visão do que se esconde por trás de uma instrução BASIC de operação cassete.
Ao se salvar ou carregar um programa em cassete, o seu micro se utiliza de várias rotinas contidas nos primeiros 16 Kbytes de ROM, que vamos chamar de BIOS. Ali se encontram todas as rotinas básicas de operação do MSX, desde o controle do teclado, vídeo, som, até mesmo a operação em cassete.
As rotinas de cassete se dividem em dois tipos: gravação e leitura. Observe a tablea 1. Nela são relacionadas estas rotinas e dados os endereços de entrada correspondentes. Não vou descrever estas rotinas, pois são, de certa forma, complexas, envolvendo conhecimentos de linguagem de máquina e do hardware do sistema. Não pretendo, neste artigo, exigir de vocês, leitores, grande conhecimento de assembly, mas uma pequena noção é muito útil. Observe que ainda existe mais uma rotina que trata somente do estado de operação do gravador cassete.
Endereço | Nome | Função |
&HE1 | TAPION | Tape input on |
&HE4 | TAPIN | Tape input |
&HE7 | TAPIOF | Tape input off |
&HEA | TAPOON | Tape output on |
&HED | TAPOOUT | Tape output |
&HF0 | TAPOOF | Tape output off |
&HF3 | STMOTR | Turn motor on/off |
Entre com os seguintes POKEs, pelo BASIC, para testar esta rotina:
POKE &HE000,&H3E
POKE &HE001,1
POKE &HE002,&HCD
POKE &HE003,&HFE
POKE &HE004,0
POKE &HE005,&HC9
ou o seguinte programa em Assembly:
ORG #E000 ;Entry Point
LD a,#01 ;#01 ON, #00 OFF, #FF INVERTE
CALL STMOTR ;Liga o Motor do cassete
RET ;Retorna ao BASIC
Digite em seguida:
DEFUSR=&HE000 : A=USR(0)
Reparou no "click" do gravador sendo acionado? Experimente dar POKE &HE001,0 e, a seguir, novamente A=USR(0). O gravador deve desligar agora. Estes POKEs, acima, somente carregar a partir do endereco &HE000, um pequeno programa em linguagem de máguina que coloca no registrador A do Z80 um valor e chamam a rotina de operação do motor do gravador. Isto equivale às instruções BASIC: MOTOR ON e MOTOR OFF.
Não é muito difícil controlar o sistema de gravação em assembly, mas deve ser tomado muito cuidado para não se gravar ou ler informações não coerentes. Uma simples questão de tempo, ou seja, demora na leitura de um byte na fita, pode tirar todo o sentido do que o computador está lendo. As rotinas do BASIC são escritas de forma a não permitir que coisas do tipo acontecam, mas você também pode fazer o mesmo sem problemas.
Como exemplo de utilização destas rotinas, dois programas são fornecios nas listagens 1 e 2. O primeiro deles gravará uma mensagem na fita cassete e o segundo lerá esta mensagem de volta. Os programas são em BASIC, contendo rotinas em linguagem de máquina em linhas DATA. As rotinas em linguagem de máquina estão escritas em assembler, respectivamente nas listagens 3 e 4, para aqueles que querem se aprofundar mais no assunto. Agora digite o programa da listagem 1, prepare o gravador para salvar a mensagem e rode o programa. Este programa pedirá a você para entrar uma mensagem, carregará esta mensagem na memória e, ao chamar a rotina em linguagem de máquina, passará a mensagem para fita cassete.
Digite NEW e entre com o programa da listagem 2. Este programa fará o inverso do anterior, lendo a mensagem da fita através de uma rotina em assembly e a carregará na memória. O restante do programa BASIC se encarregará de imprimir a mensagem no vídeo. Quando estiver digitado, volte um pouco a fita cassete para o início do bloco que você salvou anteriormente e prepare-o para carregar o bloco. Rode o programa e aguarde a mensage no vídeo. Satisfeito com o resultado? Esta foi a mensagem que você digitou no primeiro programa e, apesar de ter dado um NEW, aí está ela de volta, via cassete.
Como conclusão, vemos que todos os comandos de gravação do BASIC utilizam estar rotinas de gravação do BIOS, somente lembrando que estas são bem simples, fazendo, somente, as operações mais simples, enquanto que as rotinas do interpretador BASIC já contêm testes para você poder dar um BREAK na gravação, a seleção de dados que vão ser salvos, a gravação daquele pequeno bloco, o "header" de identificação dos programas e outras tarefas mais complexas.
Nada disto lhe impede de usá-las, como foi apresentado neste artigo, ainda, ser bem trabalhadas e muita coisa pode ser criada em cima destas rotinas. De onde vocês acham que vieram os copiadores de programas que muitos usam? E aqueles jogos que só se carregam com um carregador especial contido neles mesmos?
Para aqueles que vão se aventurar, leiam as listagens 3 e 4, tentando entender o que faz cada uma das rotinas e boa sorte nas suas experiências. Em breve, voltaremos com novas explorações pelo mundo do BIOS, descobrindo o que está por trás de outras instruções do BASIC.
Listagem 1:
0 REM LISTAGEM 1
110 REM O SISTEMA DE GRAVACAO CASSETE NO MSX
115 REM REVISTA CPU - MAIO 1988
120 REM ANDRE LUIZ FRANCO DE FREITAS
130 DATA cd,ea,00,21,00,e2,7e,fe
140 DATA 00,28,08,e5,cd,ed,00,e1
150 DATA 23,18,f3,cd,f0,00,c9
160 REM CARREGA O PROGRAMA ASSEMBLY NA MEMORIA
170 FOR I=&HE000 TO &HE016
180 READ A$: A=VAL("&H"+A$)
190 POKE I,A
200 NEXT I
210 REM LE A MENSAGEM E CARREGA NA MEMORIA
220 LINE INPUT "MENSAGEM:";M$
230 FOR I=1 TO LEN(M$)
240 B$=MID$(M$,I,1)
250 POKE &HE1FF+I,ASC(B$)
260 NEXT I
270 POKE &HE1FF+I,0 : REM TERMINA MENSAGEM COM BYTE ZERO
280 REM CHAMA ROTINA EM ASSEMBLY
290 DEF USR=&HE000 : A=USR(0)
300 PRINT:PRINT"MENSAGEM GRAVADA !!!"
310 END
Listagem 2:
0 REM listagem 2
110 REM O SISTEMA DE GRAVACAO CASSETE NO MSX
120 REM ANDRE LUIZ FRANCO DE FREITAS
130 DATA cd,e1,00,21,00,e2,e5,cd
140 DATA e4,00,e1,77,23,fe,00,20
150 DATA f5,cd,e7,00,c9
160 REM CARREGA O PROGRAMA ASSEMBLY NA MEMORIA
170 FOR I=&HE100 TO &HE114
180 READ A$: A=VAL("&H"+A$)
190 POKE I,A
200 NEXT I
210 CLS : KEY OFF
220 REM CHAMA ROTINA EM ASSEMBLY
230 DEF USR=&HE100 : A=USR(0)
240 PRINT "MENSAGEM LIDA:":PRINT:PRINT
250 E=&HE200
260 A=PEEK(E)
270 IF A=0 THEN GOTO 310
280 PRINT CHR$(A);
290 E=E+1
300 GOTO 260
310 END
Listagem 3:
TAPOON equ #EA ;Saída para cassete ON
TAPOUT equ #ED ;Grava byte em cassete
TAPOOF equ #F0 ;Saída para cassete OFF
org #E000
#E000 CD EA 00 call TAPOON ;Abre para saída para cassete
#E003 21 00 E2 ld hl,#E200 ;String ficará em #E200
#E006 7E LOP1: ld a,(hl) ;Carrega um byte
#E007 FE 00 cp #00 ;Verifica final da string em memoria
#E009 28 08 jr a,FLOP1 ;Encerra o loop
#E00B E5 push hl
#E00C CD ED 00 call TAPOUT ;Grava um byte na fita
#E00F E1 pop hl
#E010 23 inc hl ;Proximo byte
#E011 18 F3 jr LOP1
#E013 CD F0 00 call TAPOOF ;Fecha saída para cassete
#E016 C9 ret
Listagem 4:
tapion equ #EA ;Entrada de cassete ON
tapin equ #E4 ;Grava byte em cassete
tapiof equ #E7 ;Saída para cassete OFF
org #E000
#E100 CD E1 00 call TAPION ;Abre para entrada de cassete
#E103 21 00 E2 ld hl,#E200 ;String ficará em #E200
#E106 E5 LOP1: push hl
#E107 CD E4 00 call TAPIN ;Carrega um byte do cassete
#E10A E1 pop hl ;Guarda em #E200
#E10B 77 ld (hl),a
#E10C 23 inc hl
#E10D FE 00 cp #00 ;Verifica final da string em fita
#E10F 20 F5 jr nz,LOP1
#E111 CD E7 00 call TAPIOF ;Fecha a entrada de cassete
#E114 C9 ret
Menus e tabelas na Screen 2
Artigo Original: Andre L.F.de Freitas, Revisão: Wilson Pilon
A maioria dos programas existentes, principalmente os aplicativos, utilizam algum tipo de menu para que o usuário possa efetuar uma escolha entre as opções disponíveis, tornando, assim, o programa mais flexível.
Existem várias formas de se apresentar um menu, tabela ou gráfico, sendo o mais bem elaborado e de melhor resultado visual aquele no qual o menu ou tabela, ou o que desejamos apresentar, se encontra no interior de uma janela, a qual possui cor diferente do restante da tela.
Inúmeras são as possibilidades para as janelas e as instruções utilizadas para seu processamento, mesmo em Basic, são poucas e de fácil compreensão.
A principal instrução que utilizaremos é LINE com sua opção BF. Sua função é traçar uma linha, no modo gráfico, com base nas coordenadas fornecidas pelo usuário. Se a opção BF for fornecida, será traçado um retângulo, sendo que as coordenadas fornecidas correspondem aos extremos da diagonal do retângulo a ser desenhado. O retângulo também será preenchido com a cor especificada.
LINE só pode ser utilizado na tela de alta resolução, portanto, outra instrução, a SCREEN 2, será utilizada para colocar o mico no m odo de alta resolução.
Digite o programa abaixo, rodando-o a seguir.
Listagem 1:
10 SCREEN 2 : CLS
20 LINE (20,20)-(100,100),15,BF
30 GOTO 30
Na tela foi desenhada uma janela que possui cor diferente de fundo da tela.
A linha 30 se faz necessária para evitar o retorno do micro ao modo de texto.
Temos agora, ao que parece, um problema: se o micro está em modo gráfico, como colocaremos texto na tela?
Todas as telas do MSX podem ser trabalhadas como arquivos para escrita. Portanto, podemos utilizar a instrução OPEN do Basic para ter acesso à escrita em uma página gráfica. Esta instrução associa um número, entre 0 e 15, a um arquivo, no caso a página gráfica (GRP:), sendo este número associado à instrução PRINT, a qual se encarregará de escrever na tela gráfica.
Para posicionarmos o texto, usamos a instrução PSET, já que LOCATE não servirá por atuar somente nas telas de texto. PSET irá imprimir um ponto na tela e, após esta instrução, a posição de impressão corrente no vídeo é a logo a seguir às coordenadas de PSET. Se plotarmos um ponto com a cor de fundo da janela, este não será visível e ficaremos com as coordenadas de impressão do texto posicionadas no local que desejamos.
Após esta instrução, podemos usar o PRINT # para escrever no nosso "arquivo", ou seja, o vídeo gráfico.
Inclua as linhas abaixo no programa anterior e rode-o.
Listagem 2:
5 COLOR 1,4,4
15 OPEN "GRP:" FOR OUTPUT AS #1
25 PSET (30,30),15
26 PRINT #1,"TEXTO"
O programa fornecido na listagem 1 é um exemplo de menu, onde são apresentadas várias opções, cabendo ao usuário a escolha de uma delas.
Fazendo uma leitura detalhada do programa, vamos observar as coordenadas das instruções LINE e PSET. Na listagem, estas coordenadas estão sob a forma de um número multiplicado por 8. Foram apresentadas deste modo para facilitar a idéia da tela com gráficos e texto simultâneamente. A tela gráfica possui resolução de 256x192 pontos, enquanto a de texto, no modo SCREEN 1, possui resolução de 32x24 caracteres. Note que 32*8=256 e 24*8=192. Pense na tela gráfica como uma tela de texto de 32x24 caracteres. E, sempre que quiser converter as coordenadas dos caracteres, multiplique-as por oito, para utilizá-las nas instruções gráficas.
Observe também a subrotina que cria as janelas. A rotina foi melhorada com o acréscimo de instruções para criar uma "moldura" e um pequeno efeito de sombra nas janelas.
Leia atentamente o programa e observe o que pode ser feito com as janelas, aproveitando as idéias aqui apresentadas em seus próprios programas.
Listagem 3:
5 COLOR 1,4,4
10 SCREEN 2 : CLS
15 OPEN "grp:" FOR OUTPUT AS #1
20 LINE (20,20)-(100,100),15,BF
25 PSET(30,30),15
26 PRINT #1,"TEXTO"
30 GOTO 30
Listagem 4:
170 SCREEN 2: COLOR 15,4,4: CLS
180 X1=8:Y1=7:X2=24:Y2=16:CM=1:CJ=10:GOSUB 770
190 X=14:Y=8:CJ=10:CC=1:M$="MENU":GOSUB 900
200 X=11:Y=10:M$="1 - TABELA":GOSUB 900
210 X=11:Y=12:M$="2 - GRAFICO":GOSUB900
220 X=11:Y=14:M$="3 - JANELAS":GOSUB900
230 X=4:Y=1:CC=15:CJ=4:M$="Exemplos de utilizacao de": GOSUB 900
240 X=4:Y=2:CC=15:M$="janelas em programas.": GOSUB 900
250 X=4:Y=20:CC=15:M$="Por Andre Freitas - 1988": GOSUB 900: CC=1
260 A$=INKEY$:IF A$="" THEN 260
270 A=VAL(A$):IF A<1 OR A>3 THEN BEEP:GOTO 260
280 ON A GOSUB 300,460,650
290 GOTO 170
300 REM ***
310 REM OPCAO 1 - TABELA
330 CLS
340 X1=2:Y1=6:X2=30:Y2=18:CJ=3:CM=1:GOSUB 770
350 X=13:Y=7:CJ=3:CC=1:M$="TABELA":GOSUB 900
360 X=4:Y=9:M$="------------------------": GOSUB 900
370 X=4:Y=10:M$="! Mes ! Vendas ! Valor !": GOSUB 900
380 X=4:Y=11:M$="------------------------": GOSUB 900
390 X=4:Y=12:M$=" Jan 100 500,00": GOSUB 900
400 X=4:Y=13:M$=" Fev 80 400,00": GOSUB 900
410 X=4:Y=14:M$=" Mar 150 750,00": GOSUB 900
420 X=4:Y=15:M$=" Abr 190 950,00": GOSUB 900
430 X=4:Y=16:M$="------------------------": GOSUB 900
440 FOR I=1 TO 4000: NEXT I
450 RETURN
460 REM ***
470 REM OPCAO 2 - GRAFICO
490 CLS
500 X1=2:Y1=2:X2=30:Y2=22:CJ=14:CM=1: GOSUB 770
510 X=12:Y=4:M$="GRAFICO": GOSUB 900
520 X=12:Y=8:M$="Vendas": GOSUB 900
530 X=4:Y=10:M$="200 -": GOSUB 900
540 X=4:Y=12:M$="150 -": GOSUB 900
550 X=4:Y=14:M$="100 -": GOSUB 900
560 X=4:Y=16:M$=" 50 -": GOSUB 900
570 X=4:Y=18:M$=" 0 --------------------":GOSUB 900
580 X=4:Y=20:M$=" Jan Fev Mar Abr": GOSUB 900
590 X1=9:Y1=14:X2=12:Y2=18:CJ=7:GOSUB 770
600 X1=14:Y1=16:X2=17:Y2=18:CJ=9:GOSUB 770
610 X1=19:Y1=12:X2=22:Y2=18:CJ=2:GOSUB 770
620 X1=24:Y1=10:X2=27:Y2=18:CJ=10:GOSUB 770
630 FOR I=1 TO 4000: NEXT I
640 RETURN
650 REM ***
660 REM OPCAO 3 - JANELAS
680 CLS
690 X=13:Y=2:CC=15:M$="JANELAS": GOSUB 900
700 X1=4:Y1=4:X2=8:Y2=22:CJ=6:GOSUB 770
710 X1=6:Y1=6:X2=18:Y2=10:CJ=2:GOSUB770
720 X1=20:Y1=4:X2=30:Y2=16:CJ=13:GOSUB 770
730 X1=11:Y1=18:X2=28:Y2=22:CJ=7:GOSUB 770
740 X1=13:Y1=8:X2=17:Y2=20:CJ=14:GOSUB 770
750 FOR I=1 TO 4000: NEXT I
760 RETURN
770 REM SUBROTINA QUE CRIA JANELAS
790 REM PARAMETROS:
800 REM X1,Y1 - COORD. INICIAIS
810 REM X2,Y2 - COORD. FINAIS
820 REM CJ - COR DA JANELA
830 REM CM - COR DA MOLDURA
850 LINE (X1*8+3,Y1*8-3)-(X2*8+3,Y2*8-3),CM,BF
860 LINE (X2*8,Y1*8)-(X2*8+3,Y2*8-3),CM,BF
870 LINE (X1*8,Y1*8)-(X2*8,Y2*8),CJ,BF
880 LINE (X1*8,Y1*8)-(X2*8,Y2*8),CM,B
890 RETURN
900 REM *****
920 REM SUBROTINA QUE ESCREVE
930 REM NA PAGINA DE ALTA RESOLUCAO
950 REM PARAMETROS:
960 REM X,Y - COORD. MENSAGEM
970 REM CJ - COR DE FUNDO
980 REM CC - COR CARACTERES
990 REM M$ - MENSAGEM
1010 OPEN "GRP:" FOR OUTPUT AS #1
1020 PSET (X*8,Y*8),CJ
1030 COLOR CC
1040 PRINT #1, M$
1050 CLOSE #1
1060 RETURN
Jawbreaker
Artigo Original: César Mattos, Revisão: Wilson Pilon
Este jogo foi baseado no velho Pac Man.
Para fazer pontos, você deverá comer todas as vitaminas espalhadas pela tela, tomando cuidado com os guardiões que tentam impedir, a todo custo, o seu objetivo.
Presta atenção na parte centra da tela e tente apanhar as super vitaminas que de vez em quando aparecem.
Para jogar, use as setas.
Listagem 1:
20 TIME=0:KEY OFF:CLS:CLEAR 1000:SCREEN 1,2:COLOR 15,1,1:CLS:ON SPRITE GOSUB 920:OPEN "grp:" FOR OUTPUT AS #1:STRIG(0) ON
30 A$="":B$="":RESTORE 1100:FOR X=1 TO 16:READ D$:A$=A$+CHR$(VAL("&B"+LEFT$(D$,8)))
40 B$=B$+CHR$(VAL("&B"+RIGHT$(D$,8)))
50 NEXT X:SPRITE$(0)=A$+B$
60 A$="":B$="":RESTORE 1270:FOR X=1 TO 16:REA DD$:A$=A$+CHR$(VAL("&B"+D$)):NEXT X:SPRITE$(1)=A$
70 'VARIAVEIS
80 TT=0:TP=0:CH=3
90 M=154:N=36:'COORDENADAS
100 'APRESENTACAO
110 GOSUB 1480
120 SCREEN 2
130 PRESET(84,76):COLOR 12:PRINT #1,"ARCADIA SOFT":PRESET(96,90):COLOR 7:PRINT #1,"apresenta"
135 PRESET(85,77):COLOR 12:PRINT #1,"ARCADIA SOFT":PRESET(97,91):COLOR 7:PRINT #1,"apresenta"
138 LINE(78,70)-(185,70):LINE(78,105)-(185,105):LINE(78,70)-(78,105):LINE(185,70)-(185,105)
139 LINE(78,71)-(185,71):LINE(78,106)-(185,106):LINE(79,71)-(79,106):LINE(186,71)-(186,106)
140 FOR V=1 TO 2:FOR X1=60 TO 180:PUT SPRITE4,(X1,50),12,0:NEXTX 1
150 FOR X=50 TO 106:PUT SPRITE 4,(190,X),12,0:NEXT X
160 FOR X1=180 TO 60 STEP -1:PUT SPRITE 4,(X1,106),12,0:NEXT X1
170 FOR X=106 TO 60 STEP -1:PUT SPRITE 4,(60,X),12,0:NEXT X
180 NEXT V
190 PUT SPRITE 4,(-30,255),12,0:IF PLAY (0) THEN GOTO 190
200 SCREEN 3:COLOR ,1:CLS
210 LINE(15,0)-(235,230),2,B
220 FOR GG=1 TO 2:Y=0:Y1=0:A=RND(1)*13+2:IF A=7 THEN A=6
230 FOR X=1 TO 10
240 COLOR A:Y1=Y1+6:Y=Y+14:PSET(6+Y+Y1,Y1+100),1:PRINT #1,MID$("Jawbreaker",X,1)
250 SOUND7,56:SOUND8,15:FOR K=1 TO 20 STEP 4:SOUND0,K*5:NEXT K
260 PUT SPRITE 4,(6+Y+Y1,100),A,0
270 NEXT X:A=RND(1)*13+2:IF A=7 THEN A=6
280 Y=Y1+Y+19:FOR X=10 TO 1 STEP -1
290 Y=Y-20:PSET(6+Y,70),1:COLOR A:PRINT #1,MID$("Jawbreaker",X,1)
300 PUT SPRITE 4,(6+Y,100),A,0
310 FOR K=20 TO 1 STEP -4:SOUND 0,K*5:NEXT K:NEXT X
320 ON STRIG GOSUB 1470
330 NEXT GG
340 GOSUB 1480:WIDTH30:SCREEN 1:COLOR 1,7,2:LOCATE 4,5:PRINT STRING$(22,223):LOCATE 6,6:PRINT "J A W B R E A K E R":LOCATE 4,7:PRINT STRING$(22,220):PUT SPRITE 4,(122,80),13,0
350 PUT SPRITE 4,(122,80),6,0
360 LOCATE1,22:PRINT "Pres. barra de espaco p/jogar";:FOR X=1 TO 2000:NEXT X:COLOR ,,1:GOTO 200
370 'CENARIO
380 COLOR 15,1:SCREEN 2:SPRITE ON:STRIG(0)OFF
390 COLOR 7:PRESET(40,1):PRINT #1,"JAWBREAKER":COLOR 12:PRESET(150,1):PRINT #1,STRING$(CH,CHR$(249)):COLOR 14:PRESET(60,175):PRINT #1,"ARCADIA SOFT 1986":COLOR 15
395 COLOR 7:PRESET(41,1):PRINT #1,"JAWBREAKER":COLOR 12:PRESET(150,1):PRINT #1,STRING$(CH,CHR$(249)):COLOR 14:PRESET(60,176):PRINT #1,"ARCADIA SOFT 1986":COLOR 15
400 FOR X=1 TO 3:LINE(30-X,12-X)-(220-X,172-X),12,B:NEXT X
410 LINE(50,30)-(200,30),4
420 LINE(50,50)-(200,50),4
430 LINE(50,70)-(200,70),4:LINE(50,90)-(200,90),4:LINE(50,110)-(200,110),4:LINE(50,130)-(200,130),4:LINE(50,150)-(200,150),4
440 FOR X=18 TO 160 STEP 20:PRESET(50,X):PRINT #1,STRING$(19,CHR$(196)):NEXT X
445 ON INTERVAL=1500 GOSUB 1450
450 'COM AND O DO JOGO
460 INTERVAL ON
470 V=RND(1)*12:W=RND(1)*12
480 FOR X=30 TO 203 STEP 8:X1=X:X2=211-X+20
490 IF V<6 THEN GOSUB 840
500 IF W<4 THEN GOSUB 850
510 IF W>8 THEN GOSUB 860
520 IF V>3 THEN GOSUB 870
530 IF W<10 THEN GOSUB 880
540 IF V<8 THEN GOSUB 890
550 IF V>5 THEN GOSUB 900
560 IF V<6 THEN GOSUB 910
570 GOSUB 720:NEXT X
580 FOR X=30 TO 203 STEP 8:X2=X:X1=211-X+20
590 IF V<6 THEN GOSUB 840
600 IF W<4 THEN GOSUB 850
610 IF W>8 THEN GOSUB 840
620 IF V>3 THEN GOSUB 870
630 IF W<10 THEN GOSUB 880
640 IF V<8 THEN GOSUB 890
650 IF V<5 THEN GOSUB 900
660 IF V<6 THEN GOSUB 910
670 GOSUB 720
680 NEXT X
690 AA=RND(1)*4
700 FOR X=1 TO 10:PUT SPRITE X,(-20,255),9,1:NEXT X:IF AA<3 THEN GOTO 450
710 GOTO 580
720 'MOV. JAW
730 INTERVAL ON:A=STICK(0):IF A=7 AND N>37 THEN N=N-8
740 IF N=124 AND M=74 AND FLAG=1 THEN GOTO 780
750 IF A=3 AND N<203 THEN N=N+8
760 IF A=1 AND POINT(N+7,M-4)<>4 AND POINT(N,M-4)<>4 AND M>17 THEN M=M-20
770 IF A=5 AND POINT(N+7,M+16)<>4 AND POINT(N,M+16)<>4 AND M<150 THEN M=M+20
780 PUT SPRITE 1,(N,M),9,1
790 IF N=124 AND M=74 AND FLAG=1 THEN PLAY "v13l32abcddcba":TT=TT+100:PT=PT+100:GOSUB 1460
800 IF POINT(N+3,M+8)=15ORPOINT(N+3,M+8)=7 THEN TP=TP+1:PT=PT+10:PRESET(N,M+4):COLOR 1:PRINT #1,CHR$(219):COLOR 15:PLAY "v13s0m1000o4c#":TT=TT+10:IF TP=151 THEN GOSUB 1480:GOSUB 1050
810 IF TT>=5000 THEN TT=0:CH=CH+1:PLAY "v15t255o4l8dl4bl8gl8dl4cl8al1gl32":COLOR 7:PRESET(40,1):PRINT #1,"JAWBREAKER":COLOR 12:PRESET(150,1):PRINT #1,STRING$(CH,CHR$(249)):COLOR 15
820 RETURN
830 'IMPRESS.DOS INIMIGOS
840 PUT SPRITE 3,(X2,30),10,0:RETURN
850 PUT SPRITE 4,(X1,10),12,0:RETURN
860 PUT SPRITE 5,(X1,50),13,0:RETURN
870 PUT SPRITE 6,(X2,70),8,0:RETURN
880 PUT SPRITE 7,(X1,90),2,0:RETURN
890 PUT SPRITE 8,(X2,110),7,0:RETURN
900 PUT SPRITE 9,(X1,130),9,0:RETURN
910 PUT SPRITE 10,(X2,150),14,0:RETURN
920 IF PLAY (0) THEN 920
930 SOUND 7,56:SPRITE OFF:SOUND 8,15:SOUND 1,0:FOR Y=50 TO 200 STEP 5:SOUND 0,Y+RND(1)*25:PUT SPRITE 1,(N,M),RND(1)*7,1:NEXT Y:SOUND 3,0
940 CH=CH-1:IF CH=0 THEN GOTO 990
950 COLOR 1:PRESET(40,1):PRINT #1,STRING$(22,CHR$(219))
960 N=124:M=74:GOSUB 720
970 COLOR 7:PRESET(40,1):PRINT #1,"JAWNREAKER":COLOR 12:PRESET(150,1):PRINT #1,STRING$(CH,CHR$(249)):COLOR 15
980 FOR S=3 TO 10:PUT SPRITE S,(-20,255),1,0:NEXT S:SPRITE ON:RETURN
990 'ROT.FINALIZACAO
1000 PRESET(82,78):COLOR 1:PRINT #1,STRING$(12,CHR$(249)):COLOR 1:PRESET(80,96):PRINT #1,STRING$(12,CHR$(249)):PRESET(84,78):COLOR 10:PRINT #1,"FIM DE JOGO":PRESET(80,100):PRINT #1,USING"####";PT;:PRINT #1,"PON TO S"
1010 PLAY "v15l16o5cco4bagfedccccc","v13l16o4ccdefgabo5ccccc","v14o3l4cccl16c"
1020 FOR Y=1 TO 10:PUT SPRITE Y,(0,0),1,0:NEXT Y:FOR Y=1 TO 1000:NEXT Y:CLOSE
1030 IF PLAY (0) THEN 1030
1040 RUN
1050 'QD. TP=152
1060 INTERVAL OFF:SPRITE OFF:FOR Y=2 TO 10:PUT SPRITE Y,(-20,255),9,1:NEXT Y
1070 PRESET(74,77):COLOR 12:PRINT #1,PT;"PON TO S":FOR X=1 TO 200:NEXT X:COLOR 1:PRESET(74,77):PRINT #1,STRING$(15,CHR$(249)):COLOR 15
1080 TP=0:N=124:M=74:PUT SPRITE 1,(N,M),9,1
1090 FOR Y=18 TO 160 STEP 20:PRESET(50,Y):PRINT #1,STRING$(19,CHR$(196)):NEXT Y:SPRITE ON:INTERVAL ON:RETURN
1100 DATA 0000000000000000
1110 DATA 0001111111111000
1120 DATA 0011111111111100
1130 DATA 0111111111111110
1140 DATA 1110001111000111
1150 DATA 1110101111010111
1160 DATA 1110001111000111
1170 DATA 1110001111000111
1180 DATA 1111111111111111
1190 DATA 1111111111111111
1200 DATA 1111111111111111
1210 DATA 1111100000011111
1220 DATA 1111100000011111
1230 DATA 1111111111111111
1240 DATA 0101010101010101
1250 DATA 1010101010101010
1260 '
1270 DATA 11111111
1280 DATA 11111111
1290 DATA 10011001
1300 DATA 10111011
1310 DATA 10011001
1320 DATA 11111111
1330 DATA 10000001
1340 DATA 11000011
1350 DATA 11100111
1360 DATA 11111111
1370 DATA 11111111
1380 DATA 01010100
1390 DATA 01010100
1400 DATA 00000000
1410 DATA 00000000
1420 DATA 00000000
1430 DATA beep:GOTO 1430
1440 DATA 00000000
1450 COLOR 7:PRESET(122,78):PRINT #1,CHR$(1);CHR$(66):FLAG=1:ON INTERVAL=300 GOSUB 1460
1460 COLOR 1:PRESET(122,78):PRINT #1,CHR$(219):FLAG=0:ON INTERVAL=1500 GOSUB 1450:RETURN
1470 COLOR ,,1:PLAY "l32c16defgabo5c":RETURN 370
1480 SOUND 7,56:PLAY "t250","t250":PLAY "d1m5000l4","s9m400l4"
1490 PLAY "o4r1","o3c8.e8.g4.g4."
1500 PLAY "o4c8.e8.g4.g4.","r1"
1510 PLAY "o4f4.e4.d4.","o3f4.e4.d4."
1520 RETURN
Dicas
Artigo Original: CPU, Revisão: Wilson Pilon
Caps Lock
Muitos programas em basic, ao solicitarem do operador uma entrada, só reconhecem o que foi digitado caso a tecla [CAPS LOCK] esteja pressionada, ou vice-versa.
Para contornarmos este problema, e evitar uma linha de programa maior do que o necessário, podemos fazer uso de uma variável do sistema, a CPAST, que indica o estado da tecla [CAPS LOCK].
Caso [CAPS LOCK] esteja ativa, teremos em &HFCAB um valor maior que zero e menor que 255. Um valor igual a zero desativa a tecla.
Exemplo:
poke &HFCAB,1 (ativa)
poke &HFCAB,0 (desativa)
Música Aleatória
Como será a música no ano 3000?
O programa abaixo poderá dar-lhe uma idéia.
Verifique que há um certo padrão. A geração dos números aleatórios que estão sob a música é feita pelo micro segundo uma rígida matemática.
O programa Música Aleatória é parte integrante do livro 100 dicas para MSX, da Editora Aleph.
100 PLAY "S0M8000","S0M8000","S0M8000"
110 L$="L"+STR$(INT(RND(-TIME)*31)*2+2)
120 X$=L$+"N"+STR$(INT(RND(-TIME)*60))
130 Y$=L$+"N"+STR$(INT(RND(-TIME)*30+50))
140 Z$=L$+"N"+STR$(INT(RND(-TIME)*16+80))
150 PLAY X$,Y$,Z$
160 GOTO 110
Auf Wiedersehen Monty
0 REM
1 REM REVISTA CPU - MAIO 1988
2 REM AUF MONTY
3 REM LEITOR PARA MIL VIDAS
10 CLS:KEYOFF:COLOR 10,1,1:SCREEN 2
20 OPEN "grp:" FOR OUTPUT AS #1
30 FOR X=1 TO 2:PSET(29+1+X,106),POINT STEP(0,0):PRINT #1,+CHR$(34)"AUF WIFDERSHEN MONTY"+CHR$(34):NEXT X
40 COLOR 11:FOR V=1 TO 2:PSET(50+1+V,140),1:PRINT #1,"1 -> IMUNIDADE TOTAL.":NEXT V
41 COLOR 11:FOR E=1 TO 2:PSET(50+1+E,160),POINT STEP(0,0):PRINT #1,"2 -> VIDAS INFINITAS.":NEXT E
42 COLOR 11:FOR U=1 TO 2:PSET(50+1+U,180),POINT STEP(0,0):PRINT #1,"3 -> NORMAL.":NEXTU:CLOSE #1
50 A$=INPUT$(1)
60 IF A$="1" THEN 100
70 IF A$="2" THEN 130
80 IF A$="3" THEN 150
90 GOTO 50
100 BLOAD "monty1",R:BLOAD "monty2",R:BLOAD"monty3",R
110 BLOAD "monty4",R:BLOAD "monty5",R
120 BLOAD "monty6":DEF USR=&H8700:POKE &HAC60,240:POKE &H9808,0:POKE &HA970,240:A=USR(0)
130 BLOAD "monty1",R:BLOAD "monty2",R:BLOAD "monty3",R:BLOAD "monty4",R
140 BLOAD "monty5",R:BLOAD "monty6":DEF USR=&H8700:POKE &HA970,204:A=USR(0)
150 BLOAD "monty1",R:BLOAD "monty2",R:BLOAD "monty3",R:BLOAD"monty4",R
160 BLOAD "monty5",R:BLOAD "monty6",R
Zanac
0 REM
1 REM ZANAC
2 REM LEITOR PARA VIDAS INFINITAS
10 CLS:KEYOFF:COLOR10,1,1:SCREEN2
20 OPEN"grp:"FOROUTPUTAS#1
30 FORX=1TO2:PSET(50+1+X,106),POINTSTEP(200,5):PRINT#1,+CHR$(34)"ZANAC"+CHR$(34):NEXTX
41 COLOR11:FORV=1TO2:PSET(50+1+V,160),POINTSTEP(0,0):PRINT#1,"1 -> VIDAS INFINITAS.":NEXTV
42 COLOR11:FORU=1TO2:PSET(50+1+U,180),POINTSTEP(0,0):PRINT#1,"2 -> NORMAL.":NEXTU:CLOSE#1
50 A$=INPUT$(1)
60 IF A$="1"THEN100
70 IF A$="2"THEN130
90 GOTO 50
100 BLOAD"zanac1.bin":POKE&H9654,0
110 DEFUSR=&HD000:A=USR(0)
120 BLOAD"zanac2.bin",R
130 BLOAD"zanac1.bin",R:BLOAD"zanac2.bin",R
Abu Simbel Profanation
0 REM
1 REM PROFANATION
2 REM LEITOR PARA VIDAS INFINITAS
10 CLS:KEYOFF:COLOR10,1,1:SCREEN2
20 OPEN"grp:"FOROUTPUTAS#1
30 FORX=1TO2:PSET(50+1+X,106),POINTSTEP(200,5):PRINT#1,+CHR$(34)"PROFANATION"+CHR$(34):NEXTX
41 COLOR11:FORV=1TO2:PSET(50+1+V,160),POINTSTEP(0,0):PRINT#1,"1 -> VIDAS INFINITAS.":NEXTV
42 COLOR11:FORU=1TO2:PSET(50+1+U,180),POINTSTEP(0,0):PRINT#1,"2 -> NORMAL.":NEXTU:CLOSE#1
50 A$=INPUT$(1)
60 IF A$="1"THEN100
70 IF A$="2"THEN150
90 GOTO 50
100 BLOAD"profana.bin",R:BLOAD"profanb.bin",R:BLOAD"profanc.bin":POKE&HC0B4,240
110 BLOAD"profand.bin",R:BLOAD"profane.bin",R
150 BLOAD"profana.bin",R:BLOAD"profanb.bin",R:BLOAD"profanc.bin"
160 BLOAD"profand.bin",R:BLOAD"profane.bin",R
Galaga
0 REM
1 REM GALAGA
2 REM LEITOR PARA MIL VIDAS
10 CLS:KEYOFF:COLOR10,1,1:SCREEN2
20 OPEN"grp:"FOROUTPUTAS#1
30 FORX=1TO2:PSET(50+1+X,106),POINTSTEP(200,5):PRINT#1,+CHR$(34)"GALAGA"+CHR$(34):NEXTX
41 COLOR11:FORV=1TO2:PSET(50+1+V,160),POINTSTEP(0,0):PRINT#1,"1 -> VIDAS INFINITAS.":NEXTV
42 COLOR11:FORU=1TO2:PSET(50+1+U,180),POINTSTEP(0,0):PRINT#1,"2 -> NORMAL.":NEXTU:CLOSE#1
50 A$=INPUT$(1)
60 IF A$="1"THEN100
70 IF A$="2"THEN150
90 GOTO 50
100 BLOAD"galaga.001",R
110 DEFUSR=&HD044:I=USR(0)
120 BLOAD"galaga .002",&H1000:POKE &H9152,0
130 FOR I=1TO 2000:NEXTI:DEFUSR=&HD099:I=USR(0)
150 BLOAD"galaga.001",R
160 DEFUSR=&HD044:I=USR(0)
170 BLOAD"galaga .002",&H1000
180 FOR I=1TO 2000:NEXTI:DEFUSR=&HD099:I=USR(0)
Bounder
0 REM
1 REM BOUNDER
2 REM LEITOR PARA VIDAS INFINITAS
10 CLS:KEYOFF:COLOR10,1,1:SCREEN2
20 OPEN"grp:"FOROUTPUTAS#1
30 FORX=1TO2:PSET(50+1+X,106),POINTSTEP(200,5):PRINT#1,+CHR$(34)"BOUNDER"+CHR$(34):NEXTX
41 COLOR11:FORV=1TO2:PSET(50+1+V,160),POINTSTEP(0,0):PRINT#1,"1 -> VIDAS INFINITAS.":NEXTV
42 COLOR11:FORU=1TO2:PSET(50+1+U,180),POINTSTEP(0,0):PRINT#1,"2 -> NORMAL.":NEXTU:CLOSE#1
50 A$=INPUT$(1)
60 IF A$="1"THEN100
70 IF A$="2"THEN110
90 GOTO 50
100 BLOAD"boun0.bin",R
106 BLOAD"boun1.bin":POKE&H8C87,200:DEFUSR=&H8700:A=USR(0)
107 BLOAD"boun2.bin",R
108 BLOAD"boun3.bin",R
109 BLOAD"boun4.bin",R
110 BLOAD"boun0.bin",R
113 BLOAD"boun1.bin",R
114 BLOAD"boun2.bin",R
115 BLOAD"boun3.bin",R
116 BLOAD"boun4.bin",R
Matemágica
Artigo Original: J.L.Fonseca, Revisão: Wilson Pilon
Autômato Celular
Esta será uma coluna onde serão apresentados programas ou fragmentos de programas ligados à área da matemática recreativa, uma área poe demais fascinante, apesar de pouco difundida entre nós.
Os nossos computadores podem não ser tão rápidos nem ter uma resolução tão alta quanto os usados em pesquisas nas universidades, mas são o suficiente para explorar alguns mundos e problemas bem interessantes. Ao longo dos meses, veremos artigos sobre gráficos de funções, criptografia, teoria dos números, lógica, autômatos, etc.
Hoje, apresentaremos um pequeno mas interessante programa sobre um organismo matemático conhecido como autômato celular. Este organismo vive num universo simulado no computador, tendo regras evolutivas próprias, definidas pelo programa. Esta classe de programas abrange um vasto campo de possibilidades, desde o programa aqui apresntado, com u m universo unidimensional com células em 156 possíveis estados evolutivos, até universos multidimensionais, onde vivem células com centenas de estados.
Um dos mais famosos deste grupo de programas é o LIFE, com um universo bidimensional e células de dois estados, o qual será apresentado em outra ocasião.
O programa da listagem 1 cria um universo unidimensional, representado pelo vetor a%(), o qual simula um círculo, ou seja, a sua última célula é adjacente à primeira. Cada uma das variáveis do vetor representa o estado evolutivo da célula.
O programa representa as gerações sucessivas em linhas consecutivas da tela no modo 3, com as cores dos pontos representando os estados evolutivos e, desto modo, podemos ver 48 gerações de 64 células simultaneamente.
Para usar o programa, responda às perguntas como indicado. O padrão inicial deve ser fornecido como pedido, sendo que as letras de A até P corresponem aos números de 0 a 15. Em seguida, responda, com um conjunto de 0s e 1s, às perguntas sobre as influências. Esta última pergunta é que governa como as células vizinhas influenciam a célula sendo testada no momento (0-sem influência, 1-influencia). Neste programa, são consideradas células vizinhas às duas células imediatamente adjacentes à célula sob teste e às duas imediatamente seguintes a estas últimas.
O programa é simples e fácil de ser entendido e modificado para condições diferentes das dadas por mim. Sinta-se, pois, à vontade para modificá-lo e divertir-se com os belos padrões gerados por ele.
Escreva-nos dando as suas sugestões e críticas, pois as mesmas serão bem vindas. E até à próxima edição, onde veremos novas curiosidades.
140 DIM B%(3): ' INFLUENCIAS
150 DIM A%(1,63): ' UNIVERSO
160 A$ = "": B$ = "": X% = 0
170 KEY OFF: CLS
180 IF X% = 0 THEN GOTO 210
190 PRINT A$: INPUT "Deseja mudar o padrao "; B$
200 IF B$="N" OR B$ = "n" THEN GOTO 250
210 X% = 1
220 CLS: PRINT SPC(7);"Entre com o padrao inicial": PRINT " ( ate 64 caracteres entre A e P )": PRINT
230 LINE INPUT A$
240 IF A$ = "" THEN GOTO 220
250 FOR I% = 0 TO 63
260 A%(0,I%) = 0
270 NEXT I%
280 IF LEN (A$)>64 THEN A$=MID$(A$,1,63)
290 FOR I% = 1 TO LEN(A$) - 1
300 A%(0,I%)=ASC(RIGHT$(A$,I%+1)) - 65
310 NEXT I%
320 PRINT: PRINT: PRINT "Escolha a influencia das celulas vizi- nhas (0/1)": PRINT
330 INPUT "I - 2 ";B%(0)
340 INPUT "I - 1 ";B%(1)
350 INPUT "I + 1 ";B%(2)
360 INPUT "I + 2 ";B%(3)
380 ' VERIFICA E CORRIGE AS INFLUENCIAS
400 FOR I%=0 TO 3
410 IF B%(I%) <> 0 THEN B%(I%)=1
420 NEXT I%
430 SCREEN 3
450 ' INICIA A EVOLUCAO DAS CELULAS
470 FOR J% = 0 TO 47
480 E% = J% MOD 2: ' INDICE DA GERACAO ANTERIOR
490 F% = (J%+1) MOD 2: ' INDICE DA PROXIMA GERACAO
500 FOR I% = 0 TO 63
510 A% = (I%+1) MOD 63
520 B% = (I%+2) MOD 63
530 C%=(I%-1)MOD63:IFC%<0THENC%=63+ C%
540 D%=(I%-2)MOD63:IFD%<0THEND%=63+D%
550 A%(F%,I%) = (A%(E%,A%) * B%(2) + A%(E%,B%) * B%(3) + A%(E%,C%) * B%(1) + A%(E%,D%) * B%(0)) MOD 15
560 PSET (I%*4,J%*4),A%(F%,I%)
570 NEXT I%
580 NEXT J%
590 BEEP
600 B$=INKEY$: IF B$ = "" THEN GOTO 600
610 GOTO 170
Links
- WebMSX: Rode os programas no WebMSX.
- cpu-01.dsk: Disco dos programas deste número.
- cpu-01.cas: Imagem cassete dos programas.
- cpu-01asc.zip: Listagens em formato texto.
- Rcpu-01.pdf: PDF Resumido com indice.
- CPU_MSX_01.pdf: PDF Original MSX Livros.
- MSX2.ORG: Revistas CPU MSX no site WWW.MSX2.ORG.
Nenhum comentário:
Postar um comentário