Tutorial programacion NES - Basico

1, 2, 3
Voy a intentar compartir mi pasion (y mis horas invertidas) por la NES en su programacion.

Para seguir el tutorial (despues de explicar los basicos) subire un pack con todo lo necesario para seguir el tutorial con mi framework para NESHLA

Yo no soy un experto de 6502 ni mucho menos, por eso mismo no explicare nada del lenguaje que no tenga que ver con el tutorial en concreto asi que si quereis seguirlo, tendreis que miraros opcodes y demas...

(Muchos datos los sacare de: http://wiki.nesdev.com aunque alli esta en ingles)

Arquitectura Basica
Antes de programar una sola linea hay que tener clara la arquitectura de la NES (la basica, sin uso de mappers)
La NES es capaz de direccionar 64KB de memoria (con mappers la cosa cambia "un poco" porque nunca se pueden ver mas de 64KB a la vez).
Direccion Tamaño Dispositivo
$0000 $0800 2KB RAM
$0800 $0800 Mirrors de la RAM
$1000 $0800 Mirrors de la RAM
$1800 $0800 Mirrors de la RAM
$2000 $0008 registros PPU
$2008 $1FF8 Mirrors de $2000 cada 8 bytes
$4000 $0018 registros NES APU y I/O
$4018 $BFE7 PRG ROM del cartucho, PRG RAM del cartucho y registros del mapper

$FFFA - vector NMI
$FFFC - vector Reset
$FFFE - vector IRQ/BRK

Nuestros programas estaran siempre (a no ser que carguemos codigo en RAM) en la parte alta $8000-$FFF9.
Y nuestras variables en los primeros $2000 con excepcion de
A) la pila, que esta en $01E0 - $01FF ya que usaremos solo 32bytes, sino se nos comeria hasta 256
y
B) la copia de la memoria de los sprites, que se puede colocar en cualquier lugar de la RAM (en incrementos de 256 en 256) y que con el framework esta situada en $0200 y ocupa 256bytes

Asi que tendremos 2048 - 256 - 32 = 1760 (menos las que se comera el framework.)
Importante, la ram que va de $0000 a $00FF permite acceder de forma mas rapida a la misma ya que la cpu se puede ahorrar un byte a la hora de escribir las instrucciones. Normalmente se utiliza esta ram para rutinas que requieran maxima velocidad. A esta zona especial se la llama Zero Page RAM.

vector NMI : puntero a la interrupcion que se ejecutara cada vblank del monitor.
vector Reset : puntero a la interrupcion que se ejecutara al hacer reset.
vector IRQ/BRK : puntero a la interrupcion que se ejecutara al ejecutar la instruccion BRK o recibir una IRQ externa
Por ahora nos interesan los 2 primeros, que son muy facilmente configurables en NESHLA como veremos mas adelante.

Configuracion de la NES para el tutorial
Todos los ejemplos aqui generados seran para una NES sin ningun tipo de mapper, es decir, cartuchos con 32KB de ROM y 8KB de CHROM, que es la configuracion mas sencilla y no requiere conocimiento de ningun mapper, si la cosa va mas alla se podran hacer pruebas con otros mappers como AXROM, UNROM, MMC1 o MMC3, que estan soportados por mi framework.

Programa basico
Una cosa muy importante a tener en cuenta cuando se programa en la NES es que si no se desactiva el video, todos los cambios que se hagan en pantalla, sprites, etc, han de hacerse DENTRO de la interrupcion NMI, para ello el framework incluye un sistema de colas para asignar tareas a la rutina NMI que ya esta configurada.

Descripcion de los ficheros de un proyecto con el framework (o lo que nos importa tocar ya que el resto lo hara el framework por nosotros):

Aquí pondre para descarga proximamente un proyecto vacio que cargará un fondo y permitirá mover un sprite por pantalla y pasare a explicar todos sus archivos.

Llegados a este punto decidme si habeis entendido algo para que pueda ir ampliando la información para que quede todo mas claro.

Continuara...
socram8888 está baneado por "incumplimiento términos y condiciones de uso"
Como nota sobre ese espacio dedicado a la ROM:
- 5000-5FFF: registros y EXRAM del mapper MMC5
- 6000-7FFF: RAM externa
- 8000-FFFF: ROM (puede estar dividida en bancos intercambiables segun el mapper)
Mil gracias. Por ahora todo entendible.
Estructura basica del Framework y explicacion de archivos importantes
La estructura de carpetas es la siguiente:
/bin: codigo fuente del framework y del NESHLA, no es necesario modificar nada aqui, pero si quereis echar un vistazo, mejor ;)
/docs: documento con informacion del NESHLA, tampoco es necesario hacer nada aqui
/tools: emuladores, editores graficos y un programa para cargar samples (que aun no he conseguido hacer funcionar :p)
/projects: aqui estaran las carpetas de nuestros proyectos, es donde hay que entrar

/projects/demo:
make.bat: genera el archivo nes en /out
runDebug.bat: ejecuta el archivo compilado en /out con fceux version debug
runProfile.bat: ejecuta el archivo compilado en /out con virtuanes version profile (permite calcular cuantos ciclos de cpu se gastan entre dos puntos)

/out: archivos compilados y la rom project.nes con nuestra demo :)
/data: archivos binarios a incluir en nuestro codigo

/src:
project.as: archivo base del proyecto, no hay que tocar nada.
/src/include/
/src/include/chr_data.h: aqui definimos los graficos que vamos a usar, en nuestro caso solo 2 archivos de 4KB
/src/include/project.h: aqui definimos varias caracteristicas del proyecto para el framework, por ahora tampoco hay que tocar nada
/src/ram/
/src/ram/sram.as: definicion de variables en la RAM externa o SAVE RAM, no definiremos ninguna
/src/ram/wram.as: definicion de variables en la RAM comun, aqui todas las variables gordas que no sean criticas
/src/ram/zram.as: definicion de variables en la RAM rapida, aqui las variables de bucles, rutinas criticas, etc...

/src/rom/
/src/rom/romdata.as: este archivo es importante, ya que definimos todos los bloques de 8KB que vamos a usar en nuestro proyecto,
para este caso, los 3 primeros bloques se utilizaran para datos y en el ultimo se pondra el codigo del framework y de la demo.
/src/rom/main.as: aqui esta el codigo principal de nuestra demo, es el codigo que explicare mas adelante


La demo
DESCARGA
Controles: Cruceta para mover al mario animado.
Select y Start: mover el escenario.

Los graficos del Drops son de un antiguo juego mio que intentaba rehacer para la NES.
Esta interesante para ver lo que se puede llegar a hacer, ¿pero por que no empezamos con algo mas sencillo como por ejemplo poner de colores la pantalla, que cambien, etc... para luego conseguir añadir un sprite, varios, hacer un fondo completo, etc...?

A modo de consejo, eh :-) es que lo veo mas básico.
Diskover escribió:Esta interesante para ver lo que se puede llegar a hacer, ¿pero por que no empezamos con algo mas sencillo como por ejemplo poner de colores la pantalla, que cambien, etc... para luego conseguir añadir un sprite, varios, hacer un fondo completo, etc...?

A modo de consejo, eh :-) es que lo veo mas básico.

El ejemplo que he subido ya hace todo eso, que es lo mas parecido a una "demo" que tenia, lo has bajado?
Puedo empezar a ir mas lento, ahora cuando tenga el siguiente post describiendo un poco la PPU. Que recomiendas exactamente para el primer "ejemplo"?
Si, me lo he bajado y ya lo he visto, pero desde luego para que te hagas una idea, viendo el código, no pillo nada.

Para tu primer ejemplo básico ya te digo: Si enseñas a cambiar el color de fondo de la pantalla, que es lo mas básico creo yo, empezaríamos bien. Luego puedes enseñar a poner un sprite en pantalla.

La cosa seria que explicaras paso a paso que hace cada linea, para que nos hagamos una idea :-) [+risas]
Diskover escribió:Si, me lo he bajado y ya lo he visto, pero desde luego para que te hagas una idea, viendo el código, no pillo nada.

Para tu primer ejemplo básico ya te digo: Si enseñas a cambiar el color de fondo de la pantalla, que es lo mas básico creo yo, empezaríamos bien. Luego puedes enseñar a poner un sprite en pantalla.

La cosa seria que explicaras paso a paso que hace cada linea, para que nos hagamos una idea :-) [+risas]

Si si, eso tengo pensado hacerlo, despues de la explicacion de la PPU
Explicare del main.as (que es el que importa) que hace cada linea.
Pues guay, por que ademas lo de la PPU de lo poco que se es un temita que se puede pillar bien

Y bueno, sobre todo, lo mas importante, que se explique todo bien, que no dudo que lo harás, por que me consta que un programa de NES, como todos los demás, debe de llevar una cabecera, debes preparar la maquina previamente, etc... cada dirección de memoria y bancos hacen una cosa, etc....
Entendiendo la PPU

Una de las partes mas complicadas de la NES es su PPU, la que dibuja toda la pantalla, vamos a ver su mapa de memoria.
A este mapa de memoria no se puede acceder directamente con la CPU.

Direcciones Tamaño Descripcion
$0000-$0FFF $1000 Pattern Table 0 [Banco CHR inferior]
$1000-$1FFF $1000 Pattern Table 1 [Banco CHR superior]
$2000-$23FF $0400 Name Table #0
$2400-$27FF $0400 Name Table #1
$2800-$2BFF $0400 Name Table #2
$2C00-$2FFF $0400 Name Table #3
$3000-$3EFF $0F00 Mirrors de $2000-$2FFF
$3F00-$3F1F $0020 Palette RAM indexes
$3F20-$3FFF $0080 Mirrors de $3F00-$3F1F

Descripcion breve:
NOTA: 1 tile es un dibujo de 8x8 pixels, en el caso de la nes con 2bpp (2 bits por pixel)
Las pattern table
Son las que contienen los tiles a usar tanto por sprites como por fondos (256 tiles por tabla).
Los fondos solo pueden usar una de las dos tablas, que se puede definir en el framework mediante el macro:
vid_enablePPU_CTRL_1(#valor)

en la demo se utiliza:
vid_enablePPU_CTRL_1(#(BACKGRND_TABLE_0|SPRITE_TABLE_1))
que asigna la tabla 0 al escenario y la tabla 1 a los sprites.
Los sprites en el caso de estar configurados a 8x8 (como en el caso de la demo, que es la configuracion por defecto) tambien pueden utilizar solo una tabla, pero si utilizamos sprites de 8x16, estos utilizaran ambas tablas ya que al utilizar 2 tiles cada indice, se puede acceder a los 512 tiles disponibles.

En el caso de la demo no tenemos que cargar estos datos ya que vienen directamente de la CHRROM, si tuvieramos un mapper con CHRRAM, tendriamos que "cargar" los graficos en la PPU antes de usarlos.

Las Name Table
Son las que definen que tiles usar para confeccionar el escenario. De serie, la NES trae memoria para almacenar 2 Name tables, hay algun juego con configuracion 4-SCREEN que añade las dos restantes, pero lo obviaremos por ser poco frecuente.
Asi que tenemos 2, que se situaran una encima de otra si el mirroring es HORIZONTAL y una al lado de otra si el mirroring es VERTICAL.
V:
01
01
H:
00
11
Para formar un "campo" de 512x480 pixels por donde podemos hacer scroll.
Cada Pantalla mide 256x240 por lo tanto tienen 32x30 tiles cada una, ocupando 960 bytes la definicion de una pantalla entera de izquierda a derecha y de arriba a abajo.
Los 64 bytes restantes son utilizados para dar "color" a los tiles, asignando 1 paleta a cada grupo de 2x2 tiles, a esto se le llama ATTRIBUTE table.
De la siguiente forma:
ABEFIJ
CDGHKL
De modo que el byte 1 da color a esos 4 tiles (ABCD) y aqui tenemos uno de los motivos porque en muchos juegos se utilizan recuadros de 16x16 pixels para el escenario.
El byte define la paleta para cada tile de la siguiente forma: DDCCBBAA donde cada pareja es una de las 4 paletas del escenario.

La paleta de colores
La paleta de colores de guarda en $3F00-$3F1F, son 32 valores pero no todos son usables, ahora explicare por que.
La NES tiene 8 paletas, 4 para el fondo y 4 para los sprites.
Para los sprites, el primer color es transparente por lo que tenemos solo 3 por sprite, lo que nos da 12 colores.
Para el fondo pasa algo similar, solo que el primer color de todas las paletas esta compartido y es el color de fondo.

Con el framework si queremos cargar una paleta de colores:
La pasamos al buffer de paletas (8 paletas, segun lo definido en #define NPALBUFFERS 8 en el archivo project.h, se puede ampliar):
   //Cargamos la paleta de mario en el buffer 0
   ldx #0
   ldy #sizeof(PALENT)
   while(not zero) {
      lda marioPal, x
      sta palletesBuffer, x
      inx
      dey
   }

Y la cargamos donde queremos:
vid_setPalette(#0, #SPRITE_PALETTES.PAL_0);

Esta funcion se encarga de copiar los datos de nuestra paleta a la PPU en la direccion que toca, genial ¿no?
Eso si, esta funcion solo se puede usar cuando no tenemos el video activado, de modo que si no es asi, hay que cargarla en la NMI, a traves del servicio de colas:
         int_queueNmiEventStart(#EFFECT.SET_PALENTRY)
         int_queueNmiEventParam(#0)
         int_queueNmiEventParam(#SPRITE_PALETTES.PAL_0)
         int_queueNmiEventEnd()


Los indices de la paleta siguen esta imagen:
Imagen
No esta recomendado utilizar los 6 colores negros de la derecha ya que podrian no funcionar bien en hardware real o incluso estropearlo.
Asi que tenemos 14*4 = 56 colores a nuestra disposicion.

En la siguiente entrega, como modificar esta memoria externa a la CPU, de la PPU, y las cabeceras de un archivo .nes (aunque esto se maneja bien desde el NESHLA)
socram8888 está baneado por "incumplimiento términos y condiciones de uso"
No se puede estropear. Lo único que ocurre si usas esos seis negros de la derecha es que son los llamados "ultranegros", y tienen un voltaje por debajo del rango aceptado con lo que algunas teles lo confunden con la señal de BLANK con lo que sale este típico efecto: http://www.youtube.com/watch?v=SIv78J3IaMk
socram8888 escribió:No se puede estropear. Lo único que ocurre si usas esos seis negros de la derecha es que son los llamados "ultranegros", y tienen un voltaje por debajo del rango aceptado con lo que algunas teles lo confunden con la señal de BLANK con lo que sale este típico efecto: http://www.youtube.com/watch?v=SIv78J3IaMk

Yo he leido en algun sitio que son peligrosos, no me arriesgare xD
wave escribió:Las Name Table
Son las que definen que tiles usar para confeccionar el escenario. De serie, la NES trae memoria para almacenar 2 Name tables, hay algun juego con configuracion 4-SCREEN que añade las dos restantes, pero lo obviaremos por ser poco frecuente.


Mmm, ¿que juegos tren esa configuración de 4-screen? ¿por que?

wave escribió:La paleta de colores
La paleta de colores de guarda en $3F00-$3F1F, son 32 valores pero no todos son usables, ahora explicare por que.
La NES tiene 8 paletas, 4 para el fondo y 4 para los sprites.


¿Se pueden cambiar o ampliar esas paletas desde el hardware de un cartucho de NES? Me refiero a si era posible ampliar el número de paletas, con colores nuevos desde algún mapper o algo así.

wave escribió:Los indices de la paleta siguen esta imagen:
Imagen
No esta recomendado utilizar los 6 colores negros de la derecha ya que podrian no funcionar bien en hardware real o incluso estropearlo.
Asi que tenemos 14*4 = 56 colores a nuestra disposicion.


¿Por que existen entonces esos seis colores negros del final?
socram8888 está baneado por "incumplimiento términos y condiciones de uso"
Deberías saber si sabes de programación que un ordenador nunca se puede estropear desde el teclado xD

http://games4nintendo.com/nes/faq.php#48
What is the right register value for black in the palette?
The official one, which Nintendo recommends is either $0E or $0F. Palette value $0D is the blackest value of them all, but it is what's known as "blacker than black". In other words, some video monitors or television screens might not accept that colour as a valid NTSC colour value. It has been said that it might even cause some video screens to interpret it as another blanking signal, and mess up their synching rates. However, I have been using $0D in my demos with no problems to my screens. Your mileage may vary, of course.

Esos colores existen por la forma en que la NES trata los colores. Cuando tú le dices por ejemplo 00 no le dices a la PPU "mira el color 00 en la tabla de colores", sino que le dices "pon el valor binario 000000 en el codificador RGB"

Más colores no se le pueden añadir, requeriría el diseño de una PPU nueva. Aunque tal vez se pudiese cambiando colores contiguos rápidamente para obtener un color intermedio aprovechando la permanencia de la visión.

4 screen mirroring se usa principalmente en los juegos más avanzados que realizan scrolling multidireccional. Para reducir los costes de fabricación de la NES redujeron de 4KB (la necesaria para el 4 screen mirroring) a 2KB la memoria interna. Entonces estos juegos que gastan 4 screen mirroring proveen su propia VRAM externa conectada al bus de la PPU
Diskover escribió:Mmm, ¿que juegos tren esa configuración de 4-screen? ¿por que?

¿Se pueden cambiar o ampliar esas paletas desde el hardware de un cartucho de NES? Me refiero a si era posible ampliar el número de paletas, con colores nuevos desde algún mapper o algo así.

¿Por que existen entonces esos seis colores negros del final? ¿como que pueden romper la consola?

1- Basicamente porque es mas sencillo programar el scrolling multidireccional al tener 4 pantallas por las que hacer scroll (aunque encarece el cartucho final al tener que ponerle 2KB extras para esto).
2- No se si es tecnicamente posible mediante un mapper pero ningun mapper de los oficiales tiene ampliacion del numero de paletas (el unico que hace algo remotamente parecido es el MMC5 al dejarte elegir una paleta en cada tile del escenario en vez de en cada bloque de 16x16 pixels, pero no aumenta el numero de colores totales)
3-Creo que tiene que ver con que los colores en NES se generan con la PPU, lo de romper la consola no lo tengo claro, lo que si esta claro es que se produce el efecto que dice socram8888.

socram8888 escribió:Deberías saber si sabes de programación que un ordenador nunca se puede estropear desde el teclado xD

Vaya que no, pues menudas leches le puedes dar con uno de estos ps2 gordotes xD
wave escribió:2- No se si es tecnicamente posible mediante un mapper pero ningun mapper de los oficiales tiene ampliacion del numero de paletas (el unico que hace algo remotamente parecido es el MMC5 al dejarte elegir una paleta en cada tile del escenario en vez de en cada bloque de 16x16 pixels, pero no aumenta el numero de colores totales)


Mmm. Interesante.

La mayoría de los juegos de NES se nota que los sprites tienen solo 3 colores+transparente, dado que normalmente los dibujan a 16x16, y a veces se saltaban ese escollo dibujando un sprite encima de otro para dar sensación de que tenia mas colores, como por ejemplo, el sprite azul de Megaman que es independiente al sprite de la cara.

Realmente son 2 sprites colocados de tal manera que parecen uno, aprovechando el color transparente. Uno de ellos, el del cuerpo, tiene los colores negro+azul+azul oscuro+transparente y el otro sprite, el de la cara, tiene solo el color carne+blanco+transparente.

Imagen

Pero no sabia yo que el MMC5 se saltaba este problema.
Diskover escribió:
wave escribió:2- No se si es tecnicamente posible mediante un mapper pero ningun mapper de los oficiales tiene ampliacion del numero de paletas (el unico que hace algo remotamente parecido es el MMC5 al dejarte elegir una paleta en cada tile del escenario en vez de en cada bloque de 16x16 pixels, pero no aumenta el numero de colores totales)


Mmm. Interesante.

La mayoría de los juegos de NES se nota que los sprites tienen solo 3 colores+transparente, dado que normalmente los dibujan a 16x16, y a veces se saltaban ese escollo dibujando un sprite encima de otro para dar sensación de que tenia mas colores, como por ejemplo, el sprite azul de Megaman que es independiente al sprite de la cara.

Realmente son 2 sprites colocados de tal manera que parecen uno, aprovechando el color transparente. Uno de ellos, el del cuerpo, tiene los colores negro+azul+azul oscuro+transparente y el otro sprite, el de la cara, tiene solo el color carne+blanco+transparente.

Imagen

Pero no sabia yo que el MMC5 se saltaba este problema.

El MMC5 es para los fondos y solo 1 paleta en cada tile.
Un tutorial muy interesante. Estoy con el primer ejemplo viendo que puedo sacar.
DarkRyoga escribió:Un tutorial muy interesante. Estoy con el primer ejemplo viendo que puedo sacar.


Flaman. La gente se empieza a animar :-)

Mas cosas interesantes:

Megaman con visualización al acceso de memoria donde podemos ver las tablas de tiles y las de sonido.
http://www.youtube.com/watch?v=oPSERZz1PvU

Lo mismo con Metroid
http://www.youtube.com/watch?v=c8uPdDT4mfQ
Diskover escribió:
DarkRyoga escribió:Un tutorial muy interesante. Estoy con el primer ejemplo viendo que puedo sacar.


Flaman. La gente se empieza a animar :-)

Mas cosas interesantes:

Megaman con visualización al acceso de memoria donde podemos ver las tablas de tiles y las de sonido.
http://www.youtube.com/watch?v=oPSERZz1PvU

Lo mismo con Metroid
http://www.youtube.com/watch?v=c8uPdDT4mfQ

Si te interesa el tema, con el fceux del pack que puse, puedes ver muchas cosas interesantes.
socram8888 está baneado por "incumplimiento términos y condiciones de uso"
El FCEU de Cah4e3 es el mejor por la cantidad de cosas raras que soporta para hacer experimentos XD
socram8888 escribió:El FCEU de Cah4e3 es el mejor por la cantidad de cosas raras que soporta para hacer experimentos XD

Toma, un caramelo.
socram8888 está baneado por "incumplimiento términos y condiciones de uso"
Te paso mi direccion por MP y me lo envias :o
Como modificar la memoria de la PPU (Paleta de colores, tiles, pattern tables, attribute tables...)
Lo primero que hay que hacer antes de intentar acceder a la memoria es resetear el registro de acceso, ya que como haremos dos escrituras para definir la direccion de destino, hay que indicar que empezaremos por la primera.
Esto se hace automaticamente al leer de $2002 (PPU.STATUS en el framework)
bit PPU.STATUS


Ahora ya podemos seleccionar la direccion que queremos modificar escribiendolo en el registro $2006 (PPU.ADDRESS en el framework) , por ejemplo, el primer color de la paleta ($3F00)
   lda #3F
   sta PPU.ADDRESS
   lda #00
   sta PPU.ADDRESS

Para escribir en la direccion que hemos seleccionado, simplemente escribimos en $2007 (PPU.IO en el framework)
lda #FE
sta PPU.IO

Automaticamente la direccion de la PPU aumentara (1 o 32 dependiendo del valor del registro $2000 (mas adelante explicare estos registros de control)), de modo que no tenemos que seleccionar la direccion cada vez que escribamos un byte si escribimos una secuencia de bytes, por ejemplo, la paleta de colores.
Si queremos esta memoria tambien se puede leer en lugar de escribir siguiendo el mismo metodo, colocando la direccion y despues haciendo la lectura de PPU.IO
lda PPU.IO
, tambien se incrementa la direccion al hacer una lectura.

Es muy importante tener en cuenta que SOLO se puede escribir en la PPU o en el VBLANK (retrazo vertical, cuando se dispara la interrupcion nmi si esta activada) o en el HBLANK (retrazo horizontal, dificil de sincronizar sin un mapper con interrpuciones) o cuando el video esta desactivado, como en el caso de la demo, que se escribe todo al inicio.
Sino conseguiremos glitches varios de video.

En el caso de mi framework hay varias operaciones que se pueden poner en cola para ejecutarse automaticamente en la nmi, como por ejemplo la paleta de colores con este codigo copiaria el buffer 0 a la paleta 0 de sprites:
         int_queueNmiEventStart(#EFFECT.SET_PALENTRY)
         int_queueNmiEventParam(#0)
         int_queueNmiEventParam(#SPRITE_PALETTES.PAL_0)
         int_queueNmiEventEnd()

Cuando termine de explicar los basicos de la NES (ya no queda tanto, que con el sonido no me voy a meter :p), intentare explicar como usar el framework para evitarse muchos problemas a la hora de programar para NES.

En la proxima entrega, scrolling y sprites.
Esto marcha.

Cuando tengamos ya lo básico ¿podrías hacer un pequeño tutorial de ejemplo paso a paso para poner la pantalla de color rojo, amarillo, azul, etc...?
Diskover escribió:Esto marcha.

Cuando tengamos ya lo básico ¿podrías hacer un pequeño tutorial de ejemplo paso a paso para poner la pantalla de color rojo, amarillo, azul, etc...?

Si :), incluso que puedas modificarlo con la cruceta, por ejemplo.
Bien, bien. Eso es lo que mas quería para empezar. Luego ya meternos con sprite, etc... La demo tuya que pusiste es muy interesante. Pienso que solo le falta la detección de colisiones y ya seria bastante completa, pero vamos, de sobresaliente el curro que te pegas :-)

Ya mas tarde, cuando quede claro lo de los colores en fondo de pantalla, cambiarlos con el mando, etc... podríamos ir a los sprites, que posiblemente sea mas complejo, pero base fundamental de todo, jaja ;-)
Diskover escribió:Bien, bien. Eso es lo que mas quería para empezar. Luego ya meternos con sprite, etc... La demo tuya que pusiste es muy interesante. Pienso que solo le falta la detección de colisiones y ya seria bastante completa, pero vamos, de sobresaliente el curro que te pegas :-)

Ya mas tarde, cuando quede claro lo de los colores en fondo de pantalla, cambiarlos con el mando, etc... podríamos ir a los sprites, que posiblemente sea mas complejo, pero base fundamental de todo, jaja ;-)

Para las colisiones he estado pensando en hacer la tipica demo de las pelotas que rebotan por la pantalla (y chocan entre ellas), veremos que se puede hacer.
Mmm, pues guay. Por que eso abarca mucho mas que una simple colisión.

Algo mas sencillo: Un sprite que movemos por pantalla a nuestro antojo y uno o dos bloques dispersos que impiden al sprite pasar a través de ellos.

Ah, como curiosidad. Te dejo un vídeo de un tío que explica visualmente como lo hace el para un juego de vista superior, por ejemplo y que quede "creíble". Nos hacemos una idea lógica y nos vamos metiendo en ajo :-)

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

EDITO: También os dejo la primera parte.
http://www.youtube.com/watch?v=S5s8LmuIFS4
(mensaje borrado)
socram8888 está baneado por "incumplimiento términos y condiciones de uso"
socram8888 escribió:Por fin he podido hacer una maldita rutina sin fallos XD

Es simplemente para leer el mando del puerto 2:
   lda   *$02   ; Move last read
   sta   *$03

   lda   #1   ; Clock LATCH pin
   sta   $4017
   lda   #0
   sta   $4017

   sta   *$02   ; Clean new port read
   ldx   #8   ; Number of itinerations
readloop:
   rol   *$02   ; Rotate one bit left the current data
   lda   $4017   ; Load next bit
   and   #01   ; Delete garbage bits, ...
   ora   *$02   ; ... mix together, ...
   sta   *$02   ; ... and put back to $02

   dex      ; Decrement X and continue if not zero
   bne   readloop

En mi framework ya esta implementado, es algo tal que asi:
inline inp_strobePorts() {
   assignx(PORT_1_ADDR, #1)
   assignx(PORT_1_ADDR, #0)
}
inline inp_readPort(addr) {
   ldx #CONTROLLER_BUTTONS
   do {
      lda addr
      lsr a
      rol tmpPort
      dex
   } while(not zero)
}

Para leer el puerto 1:
inp_strobePorts()
inp_readPort(PORT_1_ADDR)

Aunque en el framework se hace algo mas, para saber que botones has pulsado, cuales tienes mantenidos y cuales has soltado. Pero ya lo explicare mas adelante cuando haga el ejemplo simple.
(mensaje borrado)
socram8888 está baneado por "incumplimiento términos y condiciones de uso"
Scrolling
El scroll, como la mayoria de operaciones que afectan al dibujado, ha de ponerse cuando estas en HBLANK (aunque aqui no se hace de la misma forma, esto ya se explicara mas adelante si llegamos al MMC3), VBLANK o con el video desactivado.

Para setear el scroll en el framework, solo hay que modificar las variables scrX y scrY y automaticamente la rutina de la NMI lo seteara al final de la misma.

Como se haria si lo quisieramos hacer manualmente?
Lo primero es hacer una lectura en $2002 (PPU.STATUS en el framework) para reinicializar el latch interno de la NES.
bit PPU.STATUS


Despues escribimos los valores de scroll en $2005 (PPU.BG_SCROLL en el framework) (X y luego Y) por ejemplo (5, 10)
lda #5
sta PPU.BG_SCROLL
lda #10
sta PPU.BG_SCROLL


Y finalmente reescribimos el valor de $2001 (PPU.CNT0 en el framework) que tengamos en nuestro buffer del registro (para elegir cual es la Name Table a partir de la cual se calcula el scroll)
lda buffer
sta PPU.CNT0


Sprites
Existen 2 maneras de modificar los sprites en la NES, la primera es accediendo a la memoria de sprites con los registros $2003 (para seleccionar el byte a leer/escribir) y $2004 (para leer /escribir el valor seleccionado), la segunda, que es la usada por el 99% de los juegos, es utilizar un buffer en RAM de la memoria de sprites y utilizar la copia por DMA de la que dispone la NES para copiarlo a la memoria de video en la PPU, este metodo lo explicare mas adelante.

La memoria de sprites ocupa 256bytes, 64 sprites, 4 bytes cada uno. Estos 4 bytes se utilizan para lo siguiente:
Byte 0: Posicion Y-1 del sprite, si se escribe un valor entre $EF y $FF el sprite no se muestra.
Byte 1: Tile a utilizar por el sprite, si los sprites estan en modo 8x16 se podria ver de la siguiente forma:
   76543210
   ||||||||
   |||||||+- Banco de tiles ($0000 o $1000)
   +++++++-- Numero de tile del sprite superior (la parte inferior contiene el siguiente tile)

Byte 2: Atributos del sprite, definidos de la siguiente forma:
   76543210
   ||||||||
   ||||||++- Paleta (4 a 7) del sprite
   |||+++--- No hacen nada
   ||+------ Prioridad (0: delante del escenario; 1: detras del escenario)
   |+------- Sprite rotado horizontalmente
   +-------- Sprite rotado verticalmente

Byte 3: Posicion X del sprite, los valores de $F9 a $FF no haran que el sprite se muestre parcialmente por la izquierda.

Copiando los sprites por DMA

La NES soporta la copia automatica de todos los valores de los sprites de la RAM a la memoria de SPRITES de manera automatica de la siguiente forma:
Primero seleccionamos que "banco" de memoria utilizaremos de la RAM, cada "banco" serian 256 posiciones de memoria:
Banco 0: $0000 - $00FF
Banco 1: $0100 - $01FF
Banco 2: $0200 - $02FF
Banco 3: $0300 - $03FF
etc
Este banco se informa al DMA escribiendo el valor del banco en $4014 (PPU.SPR_DMA en el framework)
Por ejemplo si nos reservamos el banco 3:
   lda #03
   sta PPU.SPR_DMA

Esta operacion consume 513 ciclos de reloj (importante porque en VBLANK tenemos cerca de 2200 para hacer modificaciones de video y esta operacion consume 513) y hace la copia en el momento en que se escribe el valor en el registro.

En el caso del framework la "shadowOAM" o copia de los sprites se encuentra en $0200 ya que el banco 0 es la ZRAM y es mejor utilizarla para otros menesteres y el banco 1 contiene la pila.
Y se indica que se quiere hacer la copia en la NMI con la siguiente instruccion en el codigo:
int_setUpdateSprites()


De modo que si queremos modificar la posicion del sprite 0, debemos modificar los valores de $0300 a $0303 con los valores que queramos que tenga de Y, tile, Atributos y X respectivamente y activar el update de los sprites en la NMI.

La prioridad de los sprites va del 0 (mas prioritario) al 63 (menos prioritario) de modo que el 0 sera el que se muestre al frente y el resto detras de este en orden.

Limite de 8 sprites
La NES solo dibuja 8 sprites en cada linea horizontal, si hay mas de 8 sprites en una linea los siguientes no se dibujan.
Para evitar este efecto, se intentó paliar con el "flickering" que todos conocemos de la NES, para que en lugar de haber sprites que no se muestran, se vayan rotando entre ellos y simular mas sprites por linea, el framework, si se utilizan sus metasprites (un sprite hecho de sprites mas pequeños) ya hace esta rotacion de los sprites por ti (si se activa esta funcion) pero esto ya se explicara cuando se haga algun ejemplo con sprites.

En la proxima entrega, los registros de control y status $2000, $2001 y $2002
Ok, pues impaciente con la siguiente entrega y meternos en el ajo ;-)
Los registros de control $2000, $2001 y status $2000 ( en el framework)
Los registros y sirven para "configurar" la manera en la que la nes se comporta,
con una simple escritura en ellos cambiamos el comportamiento de la misma, de la siguiente forma:
$2000 (PPU.CNT1 en el framework):
76543210
||||||||
||||||++- Direccion base de la Nametable (para el scroll)
||||||    (0 = $2000; 1 = $2400; 2 = $2800; 3 = $2C00)
|||||+--- Incremento de la direccion de VRAM al leer/escribir desde la CPU
|||||     (0: incrementar 1; 1: incrementar 32)
||||+---- Pattern table para los sprites
||||      (0: $0000; 1: $1000; se ignora en el modo de 8x16)
|||+------ Pattern table para el background (0: $0000; 1: $1000)
||+------- Tamaño de los sprites (0: 8x8; 1: 8x16)
|+-------- Seleccion de PPU master/esclava (no tiene efecto en la NES)
+--------- Generar una NMI al principio del VBLANK (0: no; 1: si)

Los bits 0 y 1 tambien se pueden ver como 2 bits extras de las coordenadas de scroll de la siguiente forma:
76543210
     ||
     |+- 1: Añade 256 a la posicion X del scroll
     +-- 1: Añade 240 a la posicion Y del scroll


En el framework, con tal de usar un buffer de este registro y ademas poder hacer otras operaciones facilmente,
se dispone de las siguientes funciones:
   vid_getPPU_CTRL_1()
   vid_setPPU_CTRL_1(valor)
   vid_enablePPU_CTRL_1(mascara)
   vid_disablePPU_CTRL_1(mascara)

Y estas constantes para hacer mas facil el seteo de propiedaes:
   CTRL_1.BCKGRND_TABLE_0
   CTRL_1.BCKGRND_TABLE_1
   CTRL_1.SPRITE_TABLE_0
   CTRL_1.SPRITE_TABLE_1
   CTRL_1.NAME_TABLE_0
   CTRL_1.NAME_TABLE_1
   CTRL_1.NAME_TABLE_2
   CTRL_1.NAME_TABLE_3
   CTRL_1.NMI
   CTRL_1.SPRITE8x16
   CTRL_1.ADDRINC32


De modo que se puede hacer una llamada tal que:
   //Sprites tabla 1 y background tabla 0
   vid_setPPU_CTRL_1(
       #(CTRL_1.BCKGRND_TABLE_0|CTRL_1.SPRITE_TABLE_1))


$2001 (PPU.CNT2 en el framework):
76543210
||||||||
|||||||+- Escala de grises (0: color normal; 1: display monocromo)
||||||+-- 1: Mostrar background en los 8 pixels a la izquierda de la pantalla; 0: ocultar
|||||+--- 1: Mostrar sprites en los 8 pixels a la izquierda de la pantalla; 0: ocultar
||||+---- 1: Mostrar background
|||+----- 1: Mostrar sprites
||+------ Intensificar rojo (y oscurecer el resto)
|+------- Intensificar verde (y oscurecer el resto)
+-------- Intensificar azul (y oscurecer el resto)


En el framework, con tal de usar un buffer de este registro y ademas poder hacer otras operaciones facilmente,
se dispone de las siguientes funciones:
   vid_getPPU_CTRL_2()
   vid_setPPU_CTRL_2(valor)
   vid_enablePPU_CTRL_2(mascara)
   vid_disablePPU_CTRL_2(mascara)

Y de las constantes:
   CTRL_2.GREEN_BCKGRND
   CTRL_2.BLUE_BCKGRND
   CTRL_2.RED_BCKGRND
   CTRL_2.SPRITES_NOCLIP
   CTRL_2.BCKGRND_NOCLIP
   CTRL_2.DISPLAY_MONO
   CTRL_2.SPRITES_VISIBLE
   CTRL_2.BCKGRND_VISIBLE

De modo que se puede hacer una llamada tal que:
   //Sprites y background visibles
   vid_setPPU_CTRL_2(
       #(CTRL_2.SPRITES_VISIBLE|CTRL_2.BCKGRND_VISIBLE))
   
   //Activar display en monocromo
   vid_enablePPU_CTRL_2(#CTRL_2.DISPLAY_MONO)


El registro $2002 (PPU.STATUS en el framework) sirve para leer el estado de algunos flags de la NES, tal que:
76543210
||||||||
|||+++++- Bits menos significativos del ultimo valor escrito a un registro de la PPU
|||       (due to register not being updated for this address)
||+------ Sprite overflow. Activo cuando hay mas de 8 sprites en una scanline,
||        parece que el comportamiento exacto es algo mas complicado, no se suele usar.
|+------- Sprite 0 Hit. Activo cuando un pixel no transparente del sprite 0 se sobrepone
|         con un pixel que no es del fondo del escenario, este bit se usa para partir
|          la pantalla en 2 secciones por ejemplo en Super Mario Bros. la puntuacion y el escenario.
|          (ya habra algun tutorial sobre esto porque tiene miga)       
+-------- VBLANK iniciado (0: no en VBLANK; 1: en VBLANK)

Hay que tener en cuenta que la lectura de $2002 tambien reinicia el latch interno de scrolling y el de la direccion de VRAM a la que escribimos y que el bit 7 (el de VBLANK) se pone a 0 una vez leido de modo que hay que guardarlos en algun buffer si se quiere usar el mismo valor varias veces.
En el framework disponemos de la funcion:
    vid_getPPU_STAT()


Y de las constantes

VBLANK_PERIOD

SPRITE_0_HIT


Para hacer comprobaciones:
    vid_getPPU_STAT()
    and #SPRITE_0_HIT

if(true) {

    //Hacer algo

}


Lectura de los mandos
La lectura de los mandos se hace secuencialmente mediante lecturas a los registrso $4016 y $4017 (puertos 1 y 2)
obteniendo un bit que indica pulsado o no pulsado en el siguiente orden:
A B SELECT START ARRIBA ABAJO IZQUIERDA DERECHA
Antes de leer los pads hay que reiniciar el buffer interno para que capture el valor actual,
eso se hace escribiendo al puerto correspondiente 1 y luego 0, de la siguiente forma (para el pad 1 por ejemplo):
   ldx    #01
   stx    $4016
   dex
   stx    $4016

Y luego la lectura del estado actual seria algo tal que asi:
   ldx #08
   read: lda    $4016
   lsr    a
   rol    buttons
   dex
   bne    read


La lectura de pads la dejaremos aqui ya que con el framework esta totalmente cubierta:
Actualizar el estado de los pads (1 y 2):
   inp_update(#(PORT_1|PORT_2))


Hacer comprobaciones con los pads:
   //Si acabamos de pulsar el boton
   lda pads[CONTROLLER.PLAYER1].pressed
   and #BUTTON.A
   if(true) {
       //Hacer algo
   }
   
   //Si es por lo menos el segundo frame en el que lo tenemos pulsado
   lda pads[CONTROLLER.PLAYER1].mantained
   and #BUTTON.A
   if(true) {
       //Hacer algo
   }
   
   //Si acabamos de soltar el boton
   lda pads[CONTROLLER.PLAYER1].released
   and #BUTTON.A
   if(true) {
       //Hacer algo
   }


Y creo que ya esta lo básico, con esto ya podemos empezar en la siguiente entrega con el primer tutorial para cambiar el color del fondo.
Bien, pues a ver que tal.

Te recalco y pido por favor que pongas explicación en el código hasta donde no haga falta para no perdernos [sonrisa] [sonrisa] [sonrisa]

Y vuelvo a darte muchísimas gracias por el curro [toctoc]
Primera demo: cambiando el color de fondo
Lo mejor es no mezclar esta version con la anterior del framework ya que tiene varios cambios, ire actualizandolo a medida que el tutorial avance intentando no romper nada, pero en esta version descomprimir en otra carpeta.
DESCARGA
MIRROR DESCARGA
main.as del proyecto backgroundColor:
interrupt.start main()
{   
   //Inicializamos el sistema   
   nes_init(#0)
   
   //Paramos el video
   vid_stop()
   
   //El background usara la tabla 0, los sprites la 1
   vid_enablePPU_CTRL_1(#(CTRL_1.BCKGRND_TABLE_0|CTRL_1.SPRITE_TABLE_1));
   //No activamos el clipping
   vid_enablePPU_CTRL_2(#CTRL_2.BCKGRND_NOCLIP);
   
   //Reseteamos el scroll
   lda #1
   sta scrX
   sta scrY
   
   //El modo de sprites sera de ignorar prioridad, para activar el OAM CYCLING      
   lda #1
   sta _vidIgnorePriority
   
   //Limpiamos los sprites
   vid_clearSprites()
   
   //Reiniciamos el video   
   vid_start();
   
   //Bucle principal, se ejecutara siempre
   forever {
      doFrame()
   }
}

inline doFrame() {
   //Leemos el joypad 1
   inp_update(#PORT_1)
   
   ldand(pads[CONTROLLER.PLAYER1].pressed, #BUTTON.UP)
   if(true) {
      ldx backColor
      inx
      cpx #$0D
      if(zero) {
         inx
      }
      stx backColor
   }
   
   ldand(pads[CONTROLLER.PLAYER1].pressed, #BUTTON.DOWN)
   if(true) {
      ldx backColor
      dex
      cpx #$0D
      if(zero) {
         dex
      }
      stx backColor
   }
   
   int_queueNmiEventStart(#EFFECT.SET_BACKCOLOR)
   int_queueNmiEventParam(backColor)
   int_queueNmiEventEnd()
      
   //Marcamos el final de la logica para que en la NMI se actualicen los cambios
   nes_setGameLogicCompleted()
   
   //Esperamos a que pase el VSYNC
   int_waitVbl()
}

A partir de aqui, preguntas preguntas preguntas!
wave escribió:Primera demo: cambiando el color de fondo
Lo mejor es no mezclar esta version con la anterior del framework ya que tiene varios cambios, ire actualizandolo a medida que el tutorial avance intentando no romper nada, pero en esta version descomprimir en otra carpeta.
DESCARGA


Megaupload dice que el archivo no está
Diskover escribió:
wave escribió:Primera demo: cambiando el color de fondo
Lo mejor es no mezclar esta version con la anterior del framework ya que tiene varios cambios, ire actualizandolo a medida que el tutorial avance intentando no romper nada, pero en esta version descomprimir en otra carpeta.
DESCARGA


Megaupload dice que el archivo no está

Vaya, a mi si que me lo baja, a ver si con mediafire:
DESCARGA
wave escribió:
Diskover escribió:
wave escribió:Primera demo: cambiando el color de fondo
Lo mejor es no mezclar esta version con la anterior del framework ya que tiene varios cambios, ire actualizandolo a medida que el tutorial avance intentando no romper nada, pero en esta version descomprimir en otra carpeta.
DESCARGA


Megaupload dice que el archivo no está

Vaya, a mi si que me lo baja, a ver si con mediafire:
DESCARGA



Ok, ahora si. Perfecto. Mañana me meto con el tema.
Que guay, un rinconcito a lo nesdev. Me gusta, a ver si tengo un rato y me lo leo con detenimiento y pruebo.

Wave sigue palante y amplialo que es de xinxeta. [oki]

Salu2x [bye]
A ver si es que estoy haciendo algo mal... pero... ¿que se supone que debería mostrarse en pantalla? Por que solo sale el color gris y no se puede cambiar con el mando.
Diskover escribió:A ver si es que estoy haciendo algo mal... pero... ¿que se supone que debería mostrarse en pantalla? Por que solo sale el color gris y no se puede cambiar con el mando.

Arriba y abajo cambian el color.
wave escribió:
Diskover escribió:A ver si es que estoy haciendo algo mal... pero... ¿que se supone que debería mostrarse en pantalla? Por que solo sale el color gris y no se puede cambiar con el mando.

Arriba y abajo cambian el color.


XD Ok, ok.

Ahora me dispongo a destripar un poco...

//Inicializamos el sistema   
   nes_init(#0)


Inicializamos el sistema ¿por que pones #0? ¿que significa eso?
Probé a cambiar el 0 por otro valor y sigue funcionando con normalidad.

//Paramos el video
   vid_stop()


He probado a borrar esta linea y sigue funcionando con normalidad.
En un ejemplo real, con un juego ¿cual seria su función?

Etc... con el resto del código de inicio.

//Bucle principal, se ejecutara siempre
   forever {
      doFrame()
   }


Y esto es lo que hace que se ejecute, una y otra vez doFrame ¿no?
Me voy a liar mucho, pero... ¿debo dar por hecho que son simples valores estandar para arrancar el sistema? ¿que siempre van a ser así?
_________________________________________________________________________

Mas cosas. Vamos con doFrame:

Entiendo que lo primero que hacemos es decirle que leemos el mando conectado en el puerto 1 poniendo inp_update(#PORT_1).

Luego pones ldand que exactamente no se que es... ¿algo así como un "cuando"? para que esté pendiente del mando 1 si se pulsa la cruceta hacia arriba.

Seguimos y le explicamos que debe de hacer cuando pulsamos en la cruceta hacia arriba mediante el IF, un bucle. Ok.

if(true) {
      ldx backColor
      inx
      cpx #$0D
      if(zero) {
         inx
      }
      stx backColor
   }


Guardamos en X lo que halla en backColor.
Incrementamos X y luego copiamos lo que hay en esa misma X en la dirección de memoria #$0D ¿?
El siguiente IF no entiendo muy bien que hace ¿incrementa X pero bajo que condición?
Y luego, el stx backColor es para volver a leer backColor y empezar de nuevo?

El siguiente paso entiendo que es lo mismo pero haciéndolo al revés, cuando se pulsa hacia abajo bajando el valor de X.

Y este resto de código... ¿también es como un estándar? ¿algo que se debe poner siempre?
int_queueNmiEventStart(#EFFECT.SET_BACKCOLOR)
   int_queueNmiEventParam(backColor)
   int_queueNmiEventEnd()
      
   //Marcamos el final de la logica para que en la NMI se actualicen los cambios
   nes_setGameLogicCompleted()
   
   //Esperamos a que pase el VSYNC
   int_waitVbl()


Gracias por tu paciencia ;-)

P.D.: He modificado el código y siguiendo el tutorial, he conseguido que cambie los colores pero cambiando lo de arriba y abajo por botón A y botón B. Una tontería, pero sigo probando cosas.
socram8888 está baneado por "incumplimiento términos y condiciones de uso"
Menudo follón... Creo que lo ideal seria aprender o C o Assembler, pero este NESHLA me parece una chapuza
socram8888 escribió:Menudo follón... Creo que lo ideal seria aprender o C o Assembler, pero este NESHLA me parece una chapuza

Eres libre de hacer tu tutorial de C o Assembler en tu propio post.

Si no te importa reducire el quote para ir respondiendo.
Diskover escribió:
//Inicializamos el sistema   
   nes_init(#0)


Inicializamos el sistema ¿por que pones #0? ¿que significa eso?
Probé a cambiar el 0 por otro valor y sigue funcionando con normalidad.

Inicialmente pensaba cargar algun tipo de configuracion incial, pero no tiene ningun uso asi que puedes poner el numero que quieras, si.

Diskover escribió:
//Paramos el video
   vid_stop()


He probado a borrar esta linea y sigue funcionando con normalidad.
En un ejemplo real, con un juego ¿cual seria su función?

Etc... con el resto del código de inicio.

El codigo de inicio es el "standard", es posible que en el emulador siga funcionando bien pero en teoria, ya que no puedo probar nada en hardware, intento seguir las recomendaciones para que despues funcione.
Paro el video porque es lo mas seguro mientras tocas la PPU sin estar en el VBLANK y en el anterior ejemplo aqui se cargaban datos para la pantalla de inicio y la paleta.
Alguna configuracion si que es cambiable, como si quieres clipping o no, como comente en algun post anterior.

Diskover escribió:
//Bucle principal, se ejecutara siempre
   forever {
      doFrame()
   }


Y esto es lo que hace que se ejecute, una y otra vez doFrame ¿no?
Me voy a liar mucho, pero... ¿debo dar por hecho que son simples valores estandar para arrancar el sistema? ¿que siempre van a ser así?

Si, la mayoria es estandard, contra menos cosas haya que modificar, mejor, mas adelante (si hacemos un juego o algo mas compejo) en lugar de un doFrame habra que implementar algun sistema de estados, pantallaPrincipal, menu, juego, etc...

Diskover escribió:Mas cosas. Vamos con doFrame:

Entiendo que lo primero que hacemos es decirle que leemos el mando conectado en el puerto 1 poniendo inp_update(#PORT_1).

Luego pones ldand que exactamente no se que es... ¿algo así como un "cuando"? para que esté pendiente del mando 1 si se pulsa la cruceta hacia arriba.

ldand es un macro tal que asi:
lda valoruno
and valor2

Carga el primer valor y hace una and con el segundo, si el resultado tiene algun bit a 1 (es decir, el boton esta pulsado) se entra en el bucle.

Diskover escribió:Seguimos y le explicamos que debe de hacer cuando pulsamos en la cruceta hacia arriba mediante el IF, un bucle. Ok.

if(true) {
      ldx backColor
      inx
      cpx #$0D
      if(zero) {
         inx
      }
      stx backColor
   }

Guardamos en X lo que halla en backColor.
Incrementamos X y luego copiamos lo que hay en esa misma X en la dirección de memoria #$0D ¿?
El siguiente IF no entiendo muy bien que hace ¿incrementa X pero bajo que condición?
Y luego, el stx backColor es para volver a leer backColor y empezar de nuevo?
El siguiente paso entiendo que es lo mismo pero haciéndolo al revés, cuando se pulsa hacia abajo bajando el valor de X.

cpx es: "comparar x con" asi que comparamos el valor del color con el $0D que era el color "malo" para saltarnoslo y seguir con los siguientes, stx backColor copia el valor del registro X (ya incrementado) a la variable que tiene el color de fondo.

Diskover escribió:Y este resto de código... ¿también es como un estándar? ¿algo que se debe poner siempre?
int_queueNmiEventStart(#EFFECT.SET_BACKCOLOR)
   int_queueNmiEventParam(backColor)
   int_queueNmiEventEnd()
      
   //Marcamos el final de la logica para que en la NMI se actualicen los cambios
   nes_setGameLogicCompleted()
   
   //Esperamos a que pase el VSYNC
   int_waitVbl()


Gracias por tu paciencia ;-)

nes_setGameLogicCompleted() y int_waitVbl() si que serán algo standard, ya que con el primero le indicamos a la rutina que se ejecuta en el VBLANK que puede hacer sus cambios y con el segundo esperamos a que ocurra esa rutina (aun puede quedar tiempo hasta que ocurra y asi sincronizamos a 60 fps(en NTSC, 50 en PAL)).
int_queueNmiEventStart(#EFFECT.SET_BACKCOLOR)
   int_queueNmiEventParam(backColor)
   int_queueNmiEventEnd()

Esto forma parte de mi framework y en siguientes versiones ni será necesario, basicamente ponemos en la cola de eventos que ha de actualizar el color de fondo al color que hay en la variable backColor

Diskover escribió:P.D.: He modificado el código y siguiendo el tutorial, he conseguido que cambie los colores pero cambiando lo de arriba y abajo por botón A y botón B. Una tontería, pero sigo probando cosas.

Ma alegro, hay que ir probando cosillas :)

Diskover escribió:
socram8888 escribió:Menudo follón... Creo que lo ideal seria aprender o C o Assembler, pero este NESHLA me parece una chapuza


Mmm, no me parece tanto follón por ahora.

wave, tambien he usado lda pads[CONTROLLER.PLAYER1].mantained solo con el boton A para que cambien los colores mientras le dejo pulsado y al B lo he dejado como estaba.

Y si pones released solo cambiaran al soltar el boton :)

socram8888 escribió:Me refiero al tema de usar mezcla de Assembler y C, no al comprenderlo. Normalmente se gasta Assembler para trabajar de forma rápida y C para general, pero por lo que veo en NESHLA es totalmente al reves: cosas simples como incrementar el acumulador se hacen en Asm (obviamente con ninguna ventaja de velocidad pero con el consiguiente aumento de codigo) y cosas mas complejas como leer los mandos, que es algo un pelin mas complejo, se gasta C

Los if, while, etc se traducen en simples saltos y etiquetas, te ahorras el engorro de crear nombres de etiquetas para todo o de crear etiquetas locales. Las llamadas a funciones son simples jsr y los inline son macros.
Si quieres saber como va el lenguaje en realidad: http://neshla.sourceforge.net/
socram8888 escribió:Menudo follón... Creo que lo ideal seria aprender o C o Assembler, pero este NESHLA me parece una chapuza


Mmm, no me parece tanto follón por ahora. Si sabes o tienes idea de como hacerlo de otra manera, adelante, pero no me parece que esté tan mal.

wave, tambien he usado lda pads[CONTROLLER.PLAYER1].mantained solo con el boton A para que cambien los colores mientras le dejo pulsado y al B lo he dejado como estaba.

Voy a intentar que cambie cada segundo y no de golpe si dejo presiona A
socram8888 está baneado por "incumplimiento términos y condiciones de uso"
Me refiero al tema de usar mezcla de Assembler y C, no al comprenderlo. Normalmente se gasta Assembler para trabajar de forma rápida y C para general, pero por lo que veo en NESHLA es totalmente al reves: cosas simples como incrementar el acumulador se hacen en Asm (obviamente con ninguna ventaja de velocidad pero con el consiguiente aumento de codigo) y cosas mas complejas como leer los mandos, que es algo un pelin mas complejo, se gasta C
Mi mensaje sale arriba no se por que, he respondido a todo o que ha ido saliendo creo.
socram8888 está baneado por "incumplimiento términos y condiciones de uso"
El init es borrar la RAM, iniciar el mapper al banco correcto, deshabilitar las interrupciones y esperar dos frames a que la PPU se estabilice

En la Wiki de NESDev lo tienes (busca "Init code")

Es normal pues que en un emulador no haya que iniciar, porque la RAM esta en blanco y la PPU no se tiene que estabilizar
127 respuestas
1, 2, 3