Mostrando postagens com marcador Idéias. Mostrar todas as postagens
Mostrando postagens com marcador Idéias. Mostrar todas as postagens

sábado, 17 de março de 2012

Mini Expansor de Slots com CPLD

Coloquei o Expansor de Slots que encontrei no MSXPRO numa CPLD utilizando as bibliotecas de chips TTL do Quartus II da Altera. O projeto utiliza apenas 19 macro-células e portanto cabe numa EMP3032, que está disponível em encapsulamento PLCC de 44 pinos (permitindo montagem em soquete) por menos de R$7,00. 
Apesar de operar com 3,3Volts, esta família (MAX3000) tem IO compatível com TTL 5V. 


Eis o circuito desenhado no Quartus:

terça-feira, 21 de fevereiro de 2012

MSX + Arduino

A plataforma Arduino tem suas qualidades e seus defeitos, mas sem entrar nesse mérito, uma coisa é inegável: A plataforma que 'pegou'. É fácil de encontrar para comprar, e qualquer um pode programar facilmente plataforma, usando seu computador, seja ele um PC ou Mac, sem a necessidade de hardware de programação especial.

Sendo assim, eu resolvi refazer o adaptador de Nunchuck para MSX mas desta vez usando um Arduino como plataforma.

segunda-feira, 20 de fevereiro de 2012

Adaptador para teclado PS/2 - Problemas, considerações e idéias .

Antes de me aventurar num projeto de um adaptador de teclado PS/2 é importante estudar bem as particularidades do hardware do MSX.

O teclado do MSX é uma matriz de 11 linhas por 8 colunas. O MSX seleciona a linha através dos 4 bits menos significativos da porta C da PPI que vão a um decodificador BCD-Decimal (74LS145). A leitura das colunas referentes à linha selecionada é feita através da porta B da PPI.

Normalmente, uma leitura do teclado tem a seguinte estrutura:
A=linha selecionada
OUT (0AAh),A
IN A,(0A9H)
Considerando que as intruções OUT/IN executam em 12 ciclos de máquina (no MSX é inserido um 'wait' de um ciclo de clock cada ciclo M1), e que as instruções são normalmente consecutivas, dá pra se notar que o tempo disponível para a leitura de um teclado é bem estrito.

INSTRUCTION   BYTES   M1         M2        M3  
OUT (n),A     2       OCF(4+1)   OD(3)     PW(4)
IN A,(n)      2       OCF(4+1)   OD(3)     PR(4)


Para ser bem preciso, o tempo disponível entre a escrita na porta 0AAh e a leitura na porta 0A9H vai desde o meio segundo pulso de clock do ciclo M3 da instrução OUT (Port Write) até o meio do quarto pulso de clock do ciclo M3 da instrução IN, onde o Z80 efetivamente lê a porta.


Na ponta do lápis temos:
- 2,5 ciclos de clock restantes do ciclo M3 da instrução Write
- 5 ciclos de clock do 'Opcode Fetch' do ciclo M1 da instrução IN
- 3 ciclos de clock da leitura do operando da instrução IN (ciclo M2)
- 3 ciclos de clock da leitura da porta no ciclo M3 (no 3 1/2 o dado já deve estar pronto para ser lido)

Isso nos dá 13,5 ciclos de clock, o que equivale a 3,77 microssegundos numa máquina rodando a 3,58MHz .

Se quisermos ser mais estritos ainda, devemos considerar que nem todo programa precisa escrever na porta C da PPI antes de ler a porta B, pois basta escrever uma vez e ler quando necessário (por exemplo jogos que usem somente as setas cursoras e a barra de espaço).

Assim, considerando somente a leitura na porta B da PPI (0A9h) temos um tempo que vai desde o meio do segundo pulso ate o final do terceiro pulso de clock do ciclo M3 da instrução IN.
Isso equivale a 2,5 ciclos de clock ou 698 nanosegundos.

Penso em duas abordagens para o problema. A primeira é utilizar um CPLD e um microcontrolador. A CPLD é configurada como uma matriz de 10 linhas com 8 flip flops e mais um shift register capaz de endereçar a entrada de cada uma dessas 10 linhas. O microcontrolador lê o teclado PS/2 e cria internamente uma matriz de 11 bytes representando o estado de cada tecla (para o MSX). De tempos em tempos o microcontrolador desloca sequencialmente para o shift register interno cada um dos 10 bytes e apos transferir cada um dos bytes ele atualiza o estado dos flip flops da linha equivalente. A solução mais simples seria encadear todas as 11 linhas e fazer um shift register de 80 bits, mas isso poderia gerar falsos eventos caso o MSX estivesse lendo uma linha do teclado na hora em que o microcontrolador estivesse atualizando o shift register. Isso ia requerer pelo menos (11x8)+8 macrocélulas de uma CPLD, ou seja 98 macrocélulas. Uma abordagem parecida seria fazer um shift register de 88 bits com um latch de saída, mas isso ia requerer pelo menos 168 macro células.
Uma segunda abordagem utiliza somente o microcontrolador. Mas dados os tempos, qual microcontrolador utilizar? Um AVR rodando a 20MHz roda uma instrução a cada 50 nanossegundos, e pode rodar 14 instruções em 700 nanossegundos. Um PIC com mesmo clock teria uma 3 instruções apenas. Um 8051 a 12MHz nem teria como responder tão rápido. Embora seja possível utilizar microcontroladores mais rápidos, não creio que essa seja uma abordagem elegante, ainda mais que o tempo entre eventos de teclado normalmente é bem longo, da ordem de dezenas de milissegundos.

Pensando novamente, este problema de velocidade para atender a uma requisição do microprocessador não é novo, e a solução também não. O Z80 já tem um recurso para tratar esse problema que é a própria linha WAIT. Essa linha é amostrada no terceiro pulso de clock do ciclo M3 das instruções de I/O. Caso essa linha esteja em nível baixo, o Z80 vai fazendo novas amostragens até que a linha volte a nível alto (contudo deve-se tomar cuidado para não deixar o Z80 muito tempo neste estado, pois durante o "wait state" as memórias dinâmicas não sofrem refresh).

Essa segunda abordagem permite o uso de praticamente qualquer microcontrolador, basta adicionar um flip flop e um decodificador para os sinais /RD, /CS e A0 da própria PPI. O microcontrolador pode trabalhar por polling ou por interrupções, reconhecendo a mudança de estado da saída do flip flop (que vai a zero no momento em que /RD=0; /CS=0; A0=1). O microcontrolador então lê o estado dos bits 0-3 da porta C da PPI, determina qual a linha que o MSX deseja ler. Então o microcontrolador coloca na saída os bits correspondentes à coluna lida e reseta o flip flop, liberando a linha Wait. Se quisermos ser preciosistas, após liberar o Z80 basta aguardar por um tempo equivalente a um ciclo e meio de clock do Z80 e então desativar a porta de saída (bits da coluna).



sexta-feira, 3 de fevereiro de 2012

Interface MSX-Nunchuck


A interface é bem simples: Dois resistores de pull-up e alguns diodos e um capacitor para converter os +5V da porta de joystick nos +3,3V requeridos pelo Nunchuck.



O capacitor eletrolítico é soldado por baixo:


Interface encaixada no Hot Bit


quinta-feira, 2 de fevereiro de 2012

Road Fighter funcionando com Nunchuck

Finalmente tive um tempo para mexer na adaptação, e agora está funcionando.



Tive que mexer um pouquinho no código para mudar o limiar de sensibilidade em torno da posição central (era de +/- 32 e agora está +/- 20) e remapeei as direções UP DOWN LEFT RIGHT que estavam invertidas (tanto para os acelerômetros quanto para a manete analógica), e também inverti o mapeamento dos botões C e Z. O botão Z que é o maior ficou no trigger principal, ao passo em que o botão C ficou no trigger secundário.

Missão cumprida!!

Não sei quando eu vou poder me dedicar tão intensivamente ao MSX de novo, mas fico feliz de ter conseguido fazer essa adaptação.

Valeu Igor!!! Esse projetinho de final de ano nós começamos e terminamos. Acende aí a velinha pro Nosso Senhor do Bonfim e apaga a velinha do Nosso Senhor do Bom Começo, heheheh.

terça-feira, 31 de janeiro de 2012

Road Fighter para Nunchuck

Apesar de ter começado primeiro a adaptação de Road Fighter para Nunchuck eu acabei mudando um pouco o foco para o Ping Pong porque esse último exigiu uma rotina mais genérica, capaz de tratar de até dois Nunchucks simultaneamente.
Dessa forma é possível adaptar o mesmo bloco em binário que lê os nunchucks para a maioria dos jogos da Konami, bastando para isso alterar o ponto de entrada do loop infinito do jogo e o ponto de entrada da rotina que lê os joysticks.
A única modificação necesária no 'driver' J2C foi mudar o endereço de memória dos buffers, pois o Ping Pong ocupa (com variáveis) os endereços entre (E000-E3FF) enquanto o Road Fighter utiliza entre (E000-E7FF).
Segue abaixo o código do carregador em Basic + patch. Assim que possível vou experimentar na máquina real.
10 SCREEN 0:COLOR15,1,1:CLS
20 LOCATE 6,3
30 PRINT "ROAD FIGHTER PARA NUNCHUCK"
40 LOCATE12,5
50 PRINT "DANJOVIC 2012"
60 LOCATE 6,7
70 PRINT "HTTP://HOTBIT.BLOGSPOT.COM"
80 BLOAD"ROADFIGH.BIN"
90 FOR I=1 TO 2500:NEXT I
100 '
110 REM Enderecos base
120 BLOD=&H8800 ' carreg jogo
130 INIT=&HD000 ' init nunchucks
140 REQU=&HD022 ' request dados
150 '
160 'gancho loop infinito
170 POKE BLOD+&H7C,&HC3
180 POKE BLOD+&H7D,INIT AND 255
190 POKE BLOD+&H7E,INIT \ 255 AND 255
200 '
210 'gancho leitura joystick
220 POKE BLOD+&H6C5,&HCD
230 POKE BLOD+&H6C6,REQU AND 255
240 POKE BLOD+&H6C7,REQU \ 255 AND 255
250 POKE BLOD+&H6C8,&H18
260 POKE BLOD+&H6C9,&H8
270 '
280 'rotinas leitura nunchuck
290 BLOAD "NROAD.BIN"
300 '
310 'inicializa jogo
320 DEFUSR=&HC800:A=USR(0)


Segue o código do bloco em código de máquina

NROAD.BIN

D000 AF 32 06 E8 CD 9D D0 38 :41
D008 08 3A 06 E8 CB CF 32 06 :02
D010 E8 37 CD 9D D0 38 08 3A :D3
D018 06 E8 CB D7 32 06 E8 FB :AB
D020 18 FE CB 73 20 0A 3A 06 :BE
D028 E8 CB 4F 28 13 A7 18 08 :04
D030 3A 06 E8 CB 57 28 09 37 :B2
D038 CD C2 D0 CD 50 D0 FB C9 :10
D040 3E 0F CD 93 00 3E 0E F3 :EC
D048 CD 96 00 FB 2F E6 3F C9 :7B
D050 21 00 E8 1E 00 7E FE 94 :37
D058 38 02 CB DB FE 6C 30 02 :7C
D060 CB D3 23 7E FE 94 38 02 :0B
D068 CB CB FE 6C 30 02 CB C3 :C0
D070 23 7E FE 94 38 02 CB DB :13
D078 FE 6C 30 02 CB D3 23 7E :DB
D080 FE 94 38 02 CB CB FE 6C :CC
D088 30 02 CB C3 23 23 7E CB :4F
D090 4F 20 02 CB EB CB 47 20 :59
D098 02 CB E3 7B C9 CD 10 D1 :A2
D0A0 CD 67 D1 0E A4 CD 98 D1 :ED
D0A8 CD 59 D2 D8 0E 40 CD 98 :83
D0B0 D1 CD 59 D2 D8 0E 00 CD :7C
D0B8 98 D1 CD 59 D2 D0 CD 46 :44
D0C0 D1 C9 CD 10 D1 CD 67 D1 :4D
D0C8 0E A4 CD 98 D1 CD 59 D2 :E0
D0D0 D8 0E 00 CD 98 D1 CD 59 :42
D0D8 D2 D8 CD 46 D1 CD B0 D2 :DD
D0E0 CD 67 D1 0E A5 CD 98 D1 :EE
D0E8 CD 59 D2 D8 FD 21 00 E8 :D6
D0F0 2E 05 CD D5 D1 FD 71 00 :14
D0F8 FD 23 A7 CD 24 D2 2D 20 :D7
D100 F1 CD D5 D1 FD 71 00 37 :09
D108 CD 24 D2 CD 46 D1 A7 C9 :17
D110 F3 3E 0F D3 A0 DB A2 32 :62
D118 07 E8 38 15 CB B7 CB C7 :50
D120 D3 A1 CB CF D3 A1 47 CD :96
D128 46 D1 CD 46 D1 CD 46 D1 :DF
D130 C9 CB F7 CB D7 D3 A1 CB :6C
D138 DF D3 A1 47 CD 46 D1 CD :4B
D140 46 D1 CD 46 D1 C9 78 CB :07
D148 77 20 0E CB 87 D3 A1 CB :36
D150 CF D3 A1 CB C7 D3 A1 47 :90
D158 C9 CB 97 D3 A1 CB DF D3 :1C
D160 A1 CB D7 D3 A1 47 C9 78 :3F
D168 CB 77 20 16 CB CF D3 A1 :86
D170 CB C7 D3 A1 CB 87 D3 A1 :CC
D178 CB 8F D3 A1 CB C7 D3 A1 :D4
D180 47 C9 CB DF D3 A1 CB D7 :D0
D188 D3 A1 CB 97 D3 A1 CB 9F :B4
D190 D3 A1 CB D7 D3 A1 47 C9 :9A
D198 78 CB 77 20 1C 06 08 CB :CF
D1A0 21 CB 87 30 02 CB C7 D3 :0A
D1A8 A1 CB CF D3 A1 CB 8F D3 :DC
D1B0 A1 10 EC CB C7 D3 A1 47 :EA
D1B8 C9 06 08 CB 21 CB 97 30 :55
D1C0 02 CB D7 D3 A1 CB DF D3 :95
D1C8 A1 CB 9F D3 A1 10 EC CB :46
D1D0 D7 D3 A1 47 C9 78 CB 77 :15
D1D8 20 25 CB C7 D3 A1 01 00 :4C
D1E0 08 CB CF D3 A1 5F 3E 0E :C1
D1E8 D3 A0 DB A2 E6 10 ED 44 :17
D1F0 CB 11 3E 0F D3 A0 7B CB :E2
D1F8 8F D3 A1 10 E4 47 C9 CB :D2
D200 D7 D3 A1 01 00 08 CB DF :FE
D208 D3 A1 5F 3E 0E D3 A0 DB :6D
D210 A2 E6 10 ED 44 CB 11 3E :E3
D218 0F D3 A0 7B CB 9F D3 A1 :DB
D220 10 E4 47 C9 78 CB 77 20 :DE
D228 18 CB 87 D3 A1 30 02 CB :DB
D230 C7 D3 A1 CB CF D3 A1 CB :14
D238 8F D3 A1 CB C7 D3 A1 47 :50
D240 C9 CB 97 D3 A1 30 02 CB :9C
D248 D7 D3 A1 CB DF D3 A1 CB :34
D250 9F D3 A1 CB D7 D3 A1 47 :70
D258 C9 78 CB 77 20 29 CB C7 :5E
D260 D3 A1 CB CF D3 A1 E5 21 :88
D268 D0 07 47 3E 0E D3 A0 DB :B8
D270 A2 E6 10 28 06 2B 7C B5 :22
D278 20 F5 37 3E 0F D3 A0 78 :84
D280 CB 8F D3 A1 47 E1 C9 CB :8A
D288 D7 D3 A1 CB DF D3 A1 E5 :4E
D290 21 D0 07 47 3E 0E D3 A0 :FE
D298 DB A2 E6 10 28 06 2B 7C :48
D2A0 B5 20 F5 37 3E 0F D3 A0 :C1
D2A8 78 CB 9F D3 A1 47 E1 C9 :47
D2B0 E5 F5 21 6D 00 2B 7C B5 :C4
D2B8 20 FB F1 E1 C9 :B6

domingo, 29 de janeiro de 2012

Ping Pong


Depois de apanhar um pouco com algumas besteiras no código, consegui fazer funcionar a adaptação do jogo Ping Pong para uso com (ou sem) o nunchuck. Até que ficou bacaninha, mas o jogo ficou meio lento (video).

sábado, 28 de janeiro de 2012

Patch para nunchuck

Completei ontem o teste do patch para integrar o nunchuck num jogo.
O programa em Basic logo abaixo carregou o jogo, aplicou o patch, carregou as rotinas modificadas e executou o jogo, que que rodou normalmente (pois não havia nunchucks para serem detectados). Essa parte deu para desenvolver no emulador, mas agora está tudo pronto para testar no MSX real.
10 COLOR15,1,1:SCREEN2
20 DEFUSR=&H41:A=USR(0)
30 A$="* PING PONG *"
40 B$="Para Nunchuck"
50 OPEN"grp:"AS#1
60 X=128:Y!=LEN(A$)*4:Z=X-Y!:R=X+Y!
70 Q=LEN(B$)*4:W=X-Q
80 LINE(Z-5,100)-(R+3,110),4,BF
90 LINE(Z-7,98)-(R+5,112),4,B
100 PRESET(Z,102):PRINT#1,A$
110 PRESET(Z+1,102):PRINT#1,A$
120 PRESET(W,150):PRINT#1,B$
130 PRESET(W+1,150):PRINT#1,B$
140 DEFUSR=&H44:A=USR(0)
150 BLOAD"PING"
160 FOR I=1 TO 3500:NEXT I
170 '
180 REM Enderecos base
190 BLOD=&H8800 ' carreg jogo
200 INIT=&HD000 ' init nunchucks
210 REQU=&HD016 ' request dados
220 '
230 'gancho loop infinito
240 POKE BLOD+&H99,&HC3
250 POKE BLOD+&H9A,INIT AND 255
260 POKE BLOD+&H9B,INIT \ 255 AND 255
270 '
280 'gancho leitura joystick
290 POKE BLOD+&H807,&HCD
300 POKE BLOD+&H808,REQU AND 255
310 POKE BLOD+&H809,REQU \ 255 AND 255
312 POKE BLOD+&H80A,&H18
315 POKE BLOD+&H80B,&H8
320 '
330 'rotinas leitura nunchuck
340 BLOAD "PING.BIN"
350 '
360 'inicializa jogo
370 DEFUSR=&HC800:A=USR(0)


O programa tem um pouco de firulas no começo porque eu aproveitei o carregador em Basic do jogo. O importante acontece a partir da linha 180 e o código é auto explicativo.

domingo, 22 de janeiro de 2012

TXT para Mega Assembler em Python

Tenho usado o Pasmo para compilar os programas no PC, e depois gerar o bin para copiar pro disquete e carregar no MSX real. Mas como algumas coisas não são emuladas, o melhor mesmo é poder compilar direto no MSX.
Como o Mega Assembler não trabalha com arquivos TXT mas sim com um formato próprio, estudei um pouco esse formato e fiz um script em Python para converter o arquivo em assembly no formato TXT para o formato do arquivo do Mega Assembler.

O script é bem básico, mas funcionou bem nos testes que fiz.
Algumas coisas que precisam ser observadas na hora de gerar o arquivo no PC é se lembrar das caracteristicas do Mega Assembler, como o tamanho máximos dos labels (6 bytes) e a restrição de uso de alguns caracters como o "_" (underline).

Segue abaixo o script.

# -*- coding: cp1252 -*-
#
# TXT2SIMPLE
# converte arquivos Assembly em formato texto para
# arquivo compativel com o Simple Assembly.
# Daniel Jose Viana - Janeiro de 2012
# v0.01 - versao basica 11/jan/2012
#
from struct import *
from string import *

#caminho e nome do arquivo
path='c:\\Documents and Settings\\Danjovic\\Desktop\\'
file='NunJ2C.asm'

f=open(path+file,'rb')
tamanho=0
for line in f:
lline=line.strip() #remove terminador de linha
tamanho=tamanho+len(lline)+3 #2 bytes do numero da linha mais um byte do final de linha
print "Tamanho do arquivo:",tamanho


o=open(path+file+'.as','wb')
o.write('\xFE\x01\x00'+pack('<h',tamanho)+'\xFF\xFF'),
linha=0
incremento=10
f.seek(0) # reset posicao do arquivo
for line in f:
lline=line.strip() # remove terminador de linha
linha=linha+incremento # incrementa contador
print linha,lline #
o.write(pack('<h',linha)), # escreve numero da linha
o.write (lline), # escreve linha
o.write('\x00'), # escreve terminador (zero)

o.close() # fecha arquivos
f.close()

sexta-feira, 20 de janeiro de 2012

Jogos da Konami com Nunchuck

Estive vendo que alguns jogos da Konami são bem parecidos na inicialização e na leitura dos joysticks. Então o procedimento para modifica-los para usar o Nunchuck (ou outro joystick) é bem parecido.

Basicamente, os jogos que vi iniciam a área e variáveis e depois entram num loop infinito.


...
407C EI
407D JR 0407DH
...


As rotinas de leitura de joystick têm como entrada o registrador E contendo bit 6 (ABSEL) em 1 ou em 0 dependendo da porta de joystick que se quer ler e retornam o estado do registrador 14 do PSG com os dois bits mais significativos em zero.


...
46C5 LD A,00Fh
46C7 CALL WRTPSG
46CA LD A,0Eh
46CC DI
46CD CALL RDPSG
46D0 EI
46D1 CPL
46D2 AND 03Fh
...


Então para usar o nunchuck é necessário alterar esses dois pontos do codigo. O primeiro para colocar a rotina de inicialização do nunchuck e o segundo para fazer a leitura, retornando o estado do nunchuck mapeado para os pinos do joystick.

Um exemplo, para Road Fighter, São tres bytes que podem ser modificados para:


org 0407ch
JP INITNUNS


E rotina de leitura pode ser modificada assim

org 046c3h
CALL READNUN
JR 046d2h


Daí a rotina que inicializa os nunchucks pode inclusive sinalizar num flag se conseguiu identificar a presença do nunchuck, de forma que a rotina de leitura possa reverter para o joystick original, caso o nunchuck não responda no barramento I2C.


org 09000h
;
;
;
INITNUNS:
XOR A ; Zera A e seleciona J2C port A (CY =0)
LD (NUNFLG), A ; armazena zero em NUNFLG
CALL INITNUN ; inicia nunchuck e seta bit 1 de NUNFLG se nunch 1 ok.
JR C,INNUN1 ; Se nunchuck na porta 1 não respondeu tenta o outro
SET 1,A ; Se respondeu, marca o flag
INNUN1:
SCF ; seleciona J2C port B
CALL INITNUN ; inicia nunchuck e seta bit 1 de NUNFLG se nunch 1 ok.
JR C,INNUN2 ; Se nunchuck na porta 2 não respondeu entra no loop
SET 2,A ; Se respondeu, marca o flag

INNUN2:
EI
LOOP: JR LOOP ; loop infinito do jogo


E a rotina de leitura fica assim...

;
;
READNUN: ; Registro E como entrada. Bit 6 seleciona se porta A ou B
BIT 6,E ; Z= Porta A, NZ=Porta B
JR NZ,LEPORTB

LEPORTA: ; Processa joystick A
LD A,(NUNFLG)
BIT 1,A ; testa se há nunchuck na porta A
JR Z,READSTICK ; Lê joystick normalmente se nao houver nunchuck
AND A ; seleciona J2C port A (CY =0)
JR LEITURA ; Le e interpreta
;
LEPORTB: ; Processa joystick A
LD A,(NUNFLG)
BIT 2,A ; testa se há nunchuck na porta A
JR Z,READSTICK ; Lê joystick normalmente se nao houver nunchuck
SCF ; seleciona J2C port B (CY =1)
;
LEITURA:
CALL LENUNCH ; Lê nunchuck
CALL INTERPRETA ; Interpreta leitura (retorna 00BARLDU em A)
EI
RET


A rotina de interpretação mapeia os sensores do nunchuck da seguinte forma:

Eixo X > limiar positivo = Direita acionada
Eixo X < limiar negativo = Esquerda acionada
Eixo Y > limiar positivo = Acima acionado
Eixo Y < limiar negativo = Abaixo acionado

Acelerometro X > limiar positivo = Direita acionada
Acelerometro X < limiar negativo = Esquerda acionada
Acelerometro Y > limiar positivo = Acima acionado
Acelerometro Y < limiar negativo = Abaixo acionado

Botao C acionado = Trigger B acionado
Botao Z acionado = Trigger A acionado.

sexta-feira, 13 de janeiro de 2012

Adaptando Road Fighter para jogar com Nunchuck


Usando o MSXBlue, foi fácil localizar no código o local onde as portas de joystick são lidas no Road Fighter

46C3H LD E,08Fh
46C5H LD A,00Fh
46C7H CALL WRTPSG
46CAH LD A,0Eh
46CCH DI
46CDH CALL RDPSG
46D0H EI
46D1H CPL
46D2H AND 03Fh

Esta parte do código pode ser substituída para as chamadas para ler o nunchuck.
As rotinas de leitura têm que ser modificadas de forma a transformar a posição da alavanca, dos acelerometros e dos botoes de forma a gerar o mesmo byte ao final da rotina, ou seja:


7 6 5 4 3 2 1 0
+-----------------------------------------------+
| x | x | Joy | Joy | Joy | Joy | Joy | Joy |
¦ ¦ ¦Trg.B¦Trg.A¦Right¦Left ¦Back | Fwd |
+-----------------------------------------------+

quinta-feira, 5 de janeiro de 2012

Wii Nunchuck no MSX

Trabalho conjunto com o Igor Malaquias como projeto de férias.

http://www.youtube.com/watch?v=RNiMxM2kduk



Em 2 dias fizemos tudo (teria sido menos se tivéssemos desconfiado que a porta B do Expert que usamos estava com defeito)
O joystick está ligado diretamente na porta de joystick. Os únicos componentes utilizados foram para abaixar de 5V para 3V3 e 2 resistores de pullup.


O programa foi escrito em Basic e carrega uma rotina em Assembly que utiliza a biblioteca J2C.


segunda-feira, 12 de janeiro de 2009

Interface PS/2 para porta de Joystick

Eis um circuito de teste para ligar um dispositivo PS/2 na porta de joystick do MSX. O resistor é opcional.



No protocolo PS/2, o "host" pode deter a comunicação, mantendo a linha de clock em nível baixo por mais de 100us. Por isso foi escolhido o pino 8 da porta de joystick para a linha de clock, pois no MSX esta linha fica normalmente em nível baixo.

O sinal de clock é gerado pelo dispositivo (teclado ou mouse), e a temporização é um pouco apertada (entre 60 a 100us). Por isso, as rotinas de comunicação são um loop que se repete 11 vezes. Na recepcão os bits de partida, parada e paridade são checados apenas ao final do recebimento. Já na transmissão, estes bits são previamente empacotados.

Pelas contas que fiz para a rotina de recepção, cada amostra das linhas de dados ou clock, leva cerca de 12us. O tempo de processamento do bit, leva 14us e o retorno do loop 7us. Creio ser suficiente para atender aos 30~50us de cada fase do clock.



; JPS2
; Rotinas para interface de
; dispositivos PS/2 na porta de
; Joystick do MSX
;
; Daniel José Viana, 2008
; danjovic@hotmail.com
; http://hotbit.blogspot.com
;
; Versao 0.1 11/01/2009
;
; Licenca de uso: GNU GPL
;
; compilado com PASMO Assembler
; www.arrakis.es/~ninsesabe/pasmo/


; Endereços de I/O do PSG
PSGAD: EQU 0A0H
PSGWR: EQU 0A1H
PSGRD: EQU 0A2H

; Registros do PSG

;Register 14
; 7 6 5 4 3 2 1 0
;+-----------------------------------------------+
;¦ Cas ¦ Kbd ¦ Joy ¦ Joy ¦ Joy ¦ Joy ¦ Joy ¦ Joy ¦
;¦Input¦Mode ¦Trg.B¦Trg.A¦Right¦Left ¦Back ¦ Fwd ¦
;+-----------------------------------------------+
; data clock
;Register 15
; 7 6 5 4 3 2 1 0
;+-----------------------------------------------+
;¦Kana ¦ Joy ¦Pulse¦Pulse¦Joy B¦Joy B¦Joy A¦Joy A¦
;¦ LED ¦ Sel ¦ B ¦ A ¦Pin 7¦Pin 6¦Pin 7¦Pin 6¦
;+-----------------------------------------------+
; clock data
; Constantes definidas para porta de Joystick B

; Bits do Registro 14 do PSG
DTBIT: EQU 4 ; linha DATA, leitura
CKBIT: EQU 0 ; linha CLOCK, leitura

; Bits do Registro 15 do PSG
;JOY A
;LNDATA: EQU 0 ; Linha DATA, escrita
;LNCLOCK: EQU 4 ; Linha CLOCK, escrita

;JOY B
LNDATA: EQU 2 ; Linha DATA, escrita
LNCLOCK: EQU 5 ; Linha CLOCK, escrita
ABSEL: EQU 6 ; Seleciona porta A/B

; Constantes
DTMASK: EQU ~(1<TIMEOUT: EQU 15



; Recebe um byte
; H - Byte recebido
; Flag CY - Ligado, se houver erro
;
RXPS2: SCF
CALL MUDACLOCK
LD D,11 ; 11 bits a receber
GETBIT: LD B,TIMEOUT
SAMPBIT: IN A,(PSGRD)
BIT CKBIT,A
JR Z,SHIFTBIT ; Aguarda CLOCK baixar
DJNZ SAMPBIT
JR SAIERRO
SHIFTBIT: AND DTMASK
NEG
RR H
RR L
LD B,TIMEOUT
WAITH: IN A,(PSGRD)
BIT CKBIT,A
JR NZ,PROXBIT
DJNZ WAITH
JR SAIERRO
PROXBIT: DEC D
JR NZ,GETBIT
;
AND A ; Clear Carry
CALL MUDACLOCK
;
; Registrador H
;
; st py D7 D6 D5 D4 D3 D2
; op
;
; Registrador L
; D1 D0 sta xx xx xx xx xx
; rt

RL L
RL H
RL L
RL H
;
; Registrador H
;
; D7 D6 D5 D4 D3 D2 D1 D0
;
; Registrador L
; sta xx xx xx xx xx st py
; rt op

; Testa START bit
BIT 7,L
JR NZ,SAIERRO

; Testa STOP bit
BIT 1,L
JR Z,SAIERRO

LD A,L
AND A ; flag PO->paridade esperada
; e lima flag C
BIT 0,L ; flag Z -> paridade recebida
JP PO,TESTAIMPAR
RET Z
TESTAIMPAR: RET NZ
SAIERRO: SCF
RET

;
; Muda estado da linha CLOCK
; e seleciona registro 14 do PSG
; na saída
MUDACLOCK: LD A,15
OUT (PSGAD),A
IN A,(PSGRD)
RES LNCLOCK,A
JR NC,MUDA
SET LNCLOCK,A
MUDA: SET ABSEL,A ; JOY B
;RES ABSEL,A ; JOY A
OUT (PSGWR),A
RET

quinta-feira, 14 de fevereiro de 2008

WiznetX (correção do circuito)

Algumas pessoas já me questionaram sobre como fazer a ligação de um módulo Wiznet WIZ810MJ no MSX.



Este módulo de rede é baseado no chip WM5100 e tem dois modos de interligação: SPI e memória. No modo memória, o chipa aparece para o 'host' como uma área endereçável de 32Kbytes, de acordo com o mapa abaixo (vide data sheet):




O WM5100 é alimentado por uma tensão de 3,3Volts, mas tem tolerância a sinais TTL, o que torna bem fácil a conexão ao MSX. Além disso, os sinais de controle têem a mesma polaridade (nível ativo) do Z80.

Então, todo o hardware necessário para conectar este módulo ao MSX se resume a um decodificador e um regulador de tensão LDO, que possa fornecer ao menos 183mA.

O circuito abaixo é de uma placa de protótipo. O decodificador é um LS138, que permite mapear a placa no intervalo 0000-7FFF. A temporização dos sinais /RD e /WR teve que ser adaptada para o chip W5100 pois difere em alguns pontos do Z80, em especial o sinal /WR, que fica ativo 1 ciclo de clock após o sinal MREQ do Z80. Segundo o "data sheet" do W5100 os sinais /RD e /WR têm que acontecer simultaneamente ao sinal /CS. Eu fiz umas perguntas sobre a temporização destes sinais no site do fabricante do CHIP, pois achei meio crótica a temporização "oficial" que diz que o sinal de /RD ou de /WR deve acontecer em, no máximo 1ns após o sinal de /CS, e deve retornar a nível alto 1ns antes do sinal de /CS. Mas caso seja assim mesmo, um latch controlado pelo clock do Z80 deve resolver a situação.

O regulador é o TC1108, que tem capacidade de fornecer até 300mA.




Abaixo está uma sugestão de placa para o protótipo. O módulo Wiznet deve ser montado do lado de baixo da placa.



Como eu não tenho um módulo desses, não devo montar uma placa dessas tão cedo, mas quem se aventurar a montar, seguem dois conselhos:

-O data sheet do WM5100 estipula um tempo máximo de 1ns entre o sinal de Chip Select e o READ ou Write, o que para mim significa que os sinais têem que ser simultâneos. O decodificador do meu circuito induz um certo atraso entre o sinal SLCTSLT e o CS, que eu estimo entre 30 a 40ns. Para a leitura isso não deve ser problema, pois já que o Data sheet não estipula tempo mínimo eu suponho que o sinal de RD Possa iniciar antes do sinal CS. Já para a escrita, o Z80 atrasa em 1 ciclo de clock (280ns) o sinal de WR em relaçao ao sinal MREQ. Pode ser que isso não traga problema algum, mas sempre é bom ficar e olho.

-Quando o circuito endereça na faixa de 4000-BFFF, o pino A14 está em nível alto entre - 4000-7FFF e em nível baixo entre 8000-BFFF. Isso significa que o mapa de memória vai aparecer trocado, ou seja, as áreas do TX buffer e RXbuffer vêm primeiro, entre 4000-7FFF, e os registros vêm depois, na faixa de 8000-BFFF.

domingo, 9 de dezembro de 2007

Adaptador CF Interno

Condensando alguns projetos de ZX Spectrum ([1], [2]) e fazendo algumas medições no adaptador CF-IDE que possuo (idêntico ao modelo à direita nesta foto) cheguei à seguinte pinagem:



A interface IDE-CF pode ser alimentada pelo pino 20 a partir dos 5Vcc do micro.

O sinal Y7 é o /CS (Chip Select) na faixa de endereços 0B8h~0BFh, disponível no pino 7 de IC5 (74LS138) no HotBit (mesmo pino de IC29 no Expert).

Uma observação importante é que esta faixa de endereços é reservada para a "light pen" da SANYO, e para controle do "VHD".

sexta-feira, 16 de novembro de 2007

Pseudo DMA para o VDP

A porta de dados do VDP do MSX1 faz um incremento automático do endereço de acesso a cada instrução de transferência. Isso pode ser usado para simular um DMA no VDP. Um exemplo semelhante pode ser visto neste link.

O princípio de funcionamento consiste na manipulação dos sinais de escrita e de leitura, tanto do periférico quanto do VDP de tal forma que quando o Z80 lê um determinado endereço, o sinal de decodificação aciona tanto o sinal de leitura do periférico quando o sinal de escrita do VDP, e vice-versa para uma transferência VDP->periférico. Em ambos os casos, é utilizada a instrução de leitura do Z80, de forma que não há a necessidade de resistores de isolação de barramento, como no exemplo com o AVR. Contudo,m é importante que tal como o VDP, o periférico também possa realizar transferência em modo "burst", como é o caso de um cartão SD/MMC.






A velocidade de transferência é igual à velocidade de acesso à memória ou de acesso de I/O do Z80, ou seja, praticamente a mesma velocidade que se obteria com um acesso direto à VRAM.

Uma estimativa desta velocidade, para uma transferência de 16K, com um clock de 3,579MHz é:

LD B,0
TRANSF:
IN A,(P_DMA) (64x) 12 x 64
DJNZ TRANSF 14 / 9

Ao todo são 256 * ( (12*64) +14 ) - 5 = 200187 ciclos.

Num Z80 a 3,579MHz, isso significa 55,933ms

Então temos uma taxa de pico de aproximadamente 16K/55,933ms = 286,05Kbytes/segundo.

quarta-feira, 14 de novembro de 2007

SPI na porta de Joystick

O barramento SPI é muito utilizado como interface diversos tipos de CIS: ADCs, RTCs, DIGIPOTS, SERIAL FLASHes, e mesmo cartões SD/MMC.

Este barramento constitui-se dos seguintes sinais
MOSI - Master Out, Slave In, ou seja (Saída, do ponto de vista do MASTER)
MISO - Master IN, Slave Out (Entrada)
SCLK - Serial Clock (Saída)
SS - Slave Select

Um inconveniente do SPI é a necessidade de se utilizar um sinal SS para cada periférico conectado. Principalmente porque na porta de Joystick do MSX temos apenas 3 pinos de saída, ou seja, apenas um pino disponível para Chip Select.

A solução foi utilizar esta saída livre para realizar ao mesmo tempo o 'reset' e o 'clock' de um contador. Para distinguir um evento do outro, foi utilizada uma rede RC, de forma que um pulso mais longo provoque o 'reset' do contador, e um pulso mais curto provoque uma contagem.

Na implementação para o MSX, o pino escolhido para RESET/COUNT foi o pino 8 (pulse), que fica normalmente em 0 (zero). Para prevenir que uma contagem aconteça durante um pulso de leitura de 'paddle', uma segunda constante RC foi adicionada.

O diagrama de blocos encontra-se na figura abaixo:



E o código de seleção de dispositivo pode ser visto abaixo.

;
; Seleciona dispositivo SPI
; Entrada:
; D: número do device SPI
; Saida:
; B: estado registro 15 do psg
; Modifica:
; A, D, DI,
SELDEV:

; seleciona e salva registro e 15 do PSG
CALL SAVEPSG

; saída PULS (p 8) nível 1 por 1ms
SET SHFT,A ; PULSE 1
RES ABSEL,A ; JOY 1
OUT [PSGWR],A
CALL WAIT1MS
RES SHFT,A
OUT [PSGWR],A


; gera D pulsos
; cada pulso tem 100us de largura
; para o circuito não se confundir
; com pulso de leitura de paddle
SLDV0:
CALL WAIT100US
SET SHFT,A
OUT [PSGWR],A
CALL WAIT100US
RES SHFT,A
OUT [PSGWR],A
DEC D
JR NZ,SLDV0
; salva valor reg 15 psg
LD B,A
EI
RET

sexta-feira, 9 de novembro de 2007

Enviando dados via RS232 na porta de Joystick

Em muitos microcontroladores sem UART real é comum emular este periférico gerando os bits, um a um, via Software. Mas para fazer isso é necessário saber exatamente o tempo de execução de cada instrução, de forma a se produzir as temporizações corretas.

No MSX deve-se levar em consideração não somente os ciclos de máquina gastos com cada instrução, mas também o WAIT STATE inserido a cada ciclo M1.

O Ciclo M1 é gerado durante a busca de instruções do Z80 ("opcode fetch"), mas há algumas instruções onde esse ciclo ocorre mais de uma vez, ou seja, é necessário somar um ciclo a mais para cada ciclo M1 gerado.

Um exemplo é a instrução NEG:

INSTR BYTES M1
NEG 2 OCF(4)/OCF(4)
Esta instrução gasta ao todo 10 ciclos de máquina para ser executada no MSX.

Uma relação completa das instruções do Z80, bem como da quantidade de ciclos por instrução, e a quantidade de ciclos M1 pode ser encontrada neste link: http://www.z80.info/z80ins.txt

A rotina abaixo envia um byte recebido no registrador A pelo pino 6 da porta B de joystick.


;
; Bits do Registro 15 do PSG
;
BTXD EQU 2
ABSEL EQU 6

;
; Registros do PSG
;
PSGAD EQU 0A0H
PSGWR EQU 0A1H
PSGRD EQU 0A2H


;
; Constantes

; BaudRates
;BAUD EQU 8 ; 19200 Bauds, -0.32% erro
;BAUD EQU 14 ; 14400 Bauds, -1.8% erro
BAUD EQU 25 ; 9600 Bauds, -0.32% erro
;BAUD EQU 59 ; 4800 Bauds, -0.32% erro
;BAUD EQU 127; 2400 Bauds, -0.32% erro
;BAUD EQU 255 ;1200 Bauds, 2.6% erro



;
; A: Byte a ser transmitido
; B: Estado atual do registrador 15 do PSG
;
SND232:
LD C,A
LD A,B

; Start bit
LD L,BAUD
CALL SND0 ; 17+1 + (SEND0)

; 8 bits
LD B,08H ; 7+1
LD L,BAUD ; 8

S20:
RRC C ; 8+2
CALL C,SND1 ; 17+1 + (SEND0)/ 10+1 F
CALL NC,SND0 ; 17+1 + (SEND0)/ 10+1 F
DJNZ S20 ; 13+1 b<>0/ 8+1 b=0

; Stopbit - return line to IDLE state
LD L,BAUD ; 8
CALL SND1 ; 17+1 + (SEND0)
LD B,A ; 4+1
RET ; 10+1


SND0:
RES BTXD,A ; txd=0 8+2
OUT [PSGWR],A ; 11+1
S01: DEC L ; 4+1
JP NZ,S01 ; 10+1 ;
RET ; 10+1


SND1:
SET BTXD,A ; txd=1 8+2
OUT [PSGWR],A ; 11+1
S01: DEC L ; 4+1
JP NZ,S01 ; 10+1 ;
RET ; 10+1


E Antes de imprimir, é necessário desabilitar as interrupções e inicializar o registro 15:
PRNTJ232:
DI
LD A,15
OUT [PSGAD],A
IN A,[PSGRD]
LD [PSGSAV],A

SET ABSEL,A ; JOY B
SET BTXD,A ; SDA=1
OUT [PSGWR],A
LD B,A

quarta-feira, 8 de agosto de 2007

Layouts para Mapper Simplificada

Terminei o roteamento da placa de memória para o Hotbit e para o Expert. Fiz algumas otimizações de forma a facilitar a soldagem dos chips de DRAM, e diminuir o 'excesso' lateral da placa, para não encostar nos capacitores de desacoplamento.

Placa do Hotbit:


Placa do Expert:

domingo, 5 de agosto de 2007

Clock para Hotbit convertido em 2.0

Eu hoje retirei um VDP original de um Hotbit transformado em 2.0 através de um cartão de 80 colunas modificado, mas depois vi que o VDP antigo tinha uma única função: gerar o 'clock' pro Z80.

Projetei então um adaptador pra ser colocado onde antes era colocado o VDP original, com um oscilador de 3,578MHz e um pull-up pra linha INT.

Eis abaixo a placa já roteada



E o circuito.