Tutorial programacion NES - Basico

1, 2, 3, 4, 513
wave
MegaAdicto!!!
1.186 mensajes
desde abr 2004
en Cerdanyola del Valles
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)
wave
MegaAdicto!!!
1.186 mensajes
desde abr 2004
en Cerdanyola del Valles
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.
wave
MegaAdicto!!!
1.186 mensajes
desde abr 2004
en Cerdanyola del Valles
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]
wave
MegaAdicto!!!
1.186 mensajes
desde abr 2004
en Cerdanyola del Valles
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....
wave
MegaAdicto!!!
1.186 mensajes
desde abr 2004
en Cerdanyola del Valles
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)
1, 2, 3, 4, 513