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.