We've just updated MediaWiki and its underlying software. If anything doesn't look or work quite right, please mention it to us. --RanAS

es/Dynamic Z: Difference between revisions

From SnesLab
Jump to: navigation, search
 
(52 intermediate revisions by the same user not shown)
Line 21: Line 21:
* Modo 50% más: Permite eliminar ciertas rutinas de DMA originales del juego para poder procesar mayor cantidad de recursos dinámicos.
* Modo 50% más: Permite eliminar ciertas rutinas de DMA originales del juego para poder procesar mayor cantidad de recursos dinámicos.
* Widescreen (solo para SA-1): Permite eliminar scanlines del juego para poder procesar mayor cantidad de recursos dinámicos, esto hara que la pantalla se vea como la pantalla panorámica de las películas.
* Widescreen (solo para SA-1): Permite eliminar scanlines del juego para poder procesar mayor cantidad de recursos dinámicos, esto hara que la pantalla se vea como la pantalla panorámica de las películas.
* Mirror de 420B.
* Optimizaciones para la rutina de NMI handler.
* Optimizaciones para la rutina de NMI handler.


Line 31: Line 30:
== Comparativa entre Dynamic Z y DSX ==
== Comparativa entre Dynamic Z y DSX ==


DSX Sux
DSX tambien conocido como Dynamic Sprite Patch, es un parche para sprites dinamicos pero que trae varias desventajas.
 
* Solo permite un área en la VRAM de 128x32, esto significa que como máximo permite 4 sprites de 32x32 o 1 de 64x64.
* Utiliza un buffer para armar los cuadros de animación del sprite dinámico, lo que termina siendo lento, ya que, debe transferirlos primero a el buffer y luego transferir el Buffer a la VRAM, esto produce bastante Slowdown y es muy notorio sobretodo en LoRom.
* El hijack utilizado es riesgoso, bajo ciertas cirscunstancias puede terminar generando glitches gráficos graves.
 
Dynamic Z V3.5 se supone que dejaría obsoleto al DSX, sin embargo, el staff de SMWC nunca quizo realizar una remoderación de sprites dinámicos a pesar que Dynamic Z V3.5 tenia mayores capacidades en todo aspecto.
 
Dynamic Z V3.5 permite un area de 128x64, por lo tanto, permite el doble de sprites dinamicos, además permite tambien sprites de 48x48, dando mayor flexibilidad de uso. Si se utiliza el modo 50% más, permite 128x96, 3 veces más que el DSX, además permite sprites de 80x80. Tambien permite sprites de 112x112 y de 96x96 si se utilizan 30FPS.
 
Dynamic Z V3.75 permite lo mismo que el Dynamic Z V3.5 solo que ahora hay flexibilidad total de los tamaños, además permite incrementar la cantidad de sprites sacrificando Scanlines.
 
Dynamic Z no tiene glitches graficos y no utiliza buffer además por lo que es mucho más rapido y produce menos slowdown.


== Instalación ==
== Instalación ==


WIP
Utiliza la aplicación "Dynamic Z Installer" que viene incluida con el parche, con ella puedes seleccionar las funcionalidades que trae e instalar el parche facilemente.


== DRAdder ==
== DRAdder ==
Line 95: Line 106:


El archivo puede ser de cualquier formato, no es necesario que sea ".bin".
El archivo puede ser de cualquier formato, no es necesario que sea ".bin".
Para agregar multiples recursos por archivo deben ir separados por ",", Ejemplo:
<pre>;@GFX.bin,Tilemap1.stim</pre>


== Variables ==
== Variables ==
Hay 2 tipos de variables, estan las variables del parche y los Mirrors de PPU, las variables del parche inician en la dirección $7F0B44 con LoROM o en $418000 con SA-1, mientras que los mirrors de PPU inician en $7FB080 con LoROM o en $418B80 con SA-1.
Las variables pueden usar distinta dirección de RAM dependiendo de las funcionalidades elegidas por el usuario, por eso, se requiere usar los archivos de Defines que trae el parche o instalar el parche usando el Tool de instalación.
=== Generales ===
=== Generales ===
{| class="wikitable"
|-
! Nombre !! Descripción
|-
| DZ_Timer || Incrementa en 1 cada vez que se ejecuta el código del parche durante el NMI-Handler. Se utiliza como Timer para sprites Dinámicos o para sincronizar rutinas dinámicas evitando flickering.
|-
| DZ_MaxDataPerFrameIn16x16Tiles || Máxima cantidad de data que se puede enviar a la VRAM por frame en tiles de 16x16. Por default es 0x10. Puede ser incrementado usando el modo 50% más o sacrificando scanlines. No Modificar Manualmente.
|-
| DZ_MaxDataPerFrame || Similar a DZ_MaxDataPerFrameIn16x16Tiles pero en bytes. No modificar Manualmente.
|-
| DZ_CurrentDataSend || Cantidad de data enviada en el frame actual. No modificar manualmente, puede ser leida para saber si realizar una rutina dinamica en el frame actual. Para esto se debe revisar si lo que se quiere enviar más lo que esta en esta variable es mayor a DZ_MaxDataPerFrame.
|-
| DZ_FreeRams || Direcciones de RAM que pueden utilizarse libremente, al momento de insertar el parche se puede saber cuantas hay disponibles.
|}
=== Sprites Dinámicos ===
=== Sprites Dinámicos ===
{| class="wikitable"
! Nombre !! Tamaño !! Descripción
|-
| DZ_DS_Length || 1 || Tamaño de la lista de sprites dinámicos, Tamaño máximo de la lista es 0x30. No modificar manualmente.
|-
| DZ_DS_LastSlot || 1 || Ultimo Slot de la lista. No modificar manualmente.
|-
| DZ_DS_FirstSlot || 1 || Primer Slot de la lista. No modificar manualmente.
|-
| DZ_DS_MaxSpace || 1 || Espacio máximo asignado en la VRAM para sprites dinámicos en tiles de 16x16. Por default es 0x20.
|-
| DZ_DS_FindSpaceMethod || 1 || Si es 1 los sprites dinámicos se acomodan partiendo desde el fondo del espacio asignado en la VRAM. Si es 0 se acomodan partiendo desde el Tope del espacio asignado en la VRAM. Por Default es 1.
|-
| DZ_DS_StartingVRAMOffset || 2 || Dirección en la VRAM desde donde inicia el espacio asignado para sprites dinámicos.
|-
| DZ_DS_StartingVRAMOffset8x8Tiles || 1 || Similar a DZ_DS_StartingVRAMOffset pero en en bloques de 8x8.
|-
| DZ_DS_TotalSpaceUsed || 1 || Espacio total usado por todos los sprites dinámicos en la VRAM. No modificar manualmente.
|-
| DZ_DS_TotalSpaceUsedOdd || 1 || Espacio Total usado por todos los sprites dinámicos en frames impares. No modificar manualmente.
|-
| TotalSpaceUsedEven || 1 || Espacio Total usado por todos los sprites dinámicos en frames pares. No modificar manualmente.
|-
| DZ_DS_TotalDataSentOdd || 1 || Cantidad de data enviada a la VRAM enviada por sprites en frames impares. No modificar manualmente.
|-
| TotalDataSentEven || 1 || Cantidad de data enviada a la VRAM enviada por sprites en frames impares. No modificar manualmente.
|}
Las siguientes son tablas de 0x30 bytes que representan la información de cada sprite dinámico.
{| class="wikitable"
|-
! Nombre !! Descripción
|-
| DZ_DS_Loc_UsedBy || Se utiliza para vincular a un sprite con un Slot Dinámico. Usa el formato S TT IIIII, S = Si es Shared Dynamic o no. TT => 00 es sprite Normal, 01 Es Cluster Sprite, 10 Es Extended Sprite y 11 es Overworld Sprite. IIIII es el Indice del Sprite.
|-
| DZ_DS_Loc_SpriteNumber || Es el ID Del Sprite. Se Utiliza para saber si el sprite vinculado a el Slot sigue siendo valido.
|-
| DZ_DS_Loc_SpaceUsedOffset || Indica en que lugar de la VRAM inicia el espacio asignado al slot de sprite dinámico. Esta en bloques de 16x16.
|-
| DZ_DS_Loc_SpaceUsed || La cantidad de espacio que requiere el Sprite en la VRAM. Esta en bloques de 16x16.
|-
| DZ_DS_Loc_IsValid || Si es 0, el sprite dinámico aun no ha realizado su rutina dinámica y no por lo tanto, no puede realizar su rutina gráfica.
|-
| DZ_DS_Loc_FrameRateMethod || Si es 0 puede enviar data a la VRAM en cualquier frame, si es 1 solo en frame impares y si es 2 solo en frame pares.
|-
| DZ_DS_Loc_NextSlot || Link hacia el siguiente slot de la lista. No modificar manualmente.
|-
| DZ_DS_Loc_PreviewSlot || Link hacia el slot anterior de la lista. No modificar manualmente.
|}
Las siguientes tablas indican que slot de la lista usa cada sprite dinámico.
{| class="wikitable"
|-
! Nombre !! Tamaño
|-
| DZ_DS_Loc_US_Normal || Lorom: 12 - SA-1: 22
|-
| DZ_DS_Loc_US_Cluster || 20
|-
| DZ_DS_Loc_US_Extended || 10
|-
| DZ_DS_Loc_US_OW || 16
|}
==== Sprites Dinámicos con Memoria Compartida ====
==== Sprites Dinámicos con Memoria Compartida ====
Las siguientes son tablas de 0x30 bytes asignadas a un slot de sprite dinámico.
{| class="wikitable"
|-
! Nombre !! Descripción
|-
| DZ_DS_Loc_SharedUpdated || Indica si el el slot de sprite dinámico ya realizo su rutina dinámica durante este frame, después de procesar todas las copias del sprite dinámico con memoria compartida, se pondrá en 0.
|-
| DZ_DS_Loc_SharedFrame || Indica que cuadro de animación deben mostrar todas las copias del sprite dinámico con memoria compartida.
|}
==== Sprites Semi-Dinámicos ====
==== Sprites Semi-Dinámicos ====
Las variables de Sprites Semi-Dinámicos estan conformadas por una tablas por cada tipo de sprite.
Offset indica la posición en la VRAM en la que se alojaran los gráficos del sprite.
0x00 = X
0x01 = X+$100
0x02 = X+$200
0x03 = X+$300
0x04 = X+$400
0x05 = X+$500
0x06 = X+$600
0x07 = X+$700
0x08 = X+$800
0x09 = X+$900
0x0A = X+$A00
0x0B = X+$B00
0x0C = X+$C00
0x0D = X+$D00
0x0E = X+$E00
0x0F = X+$F00
X puede ser $6000 si se elije SP1/SP2 o $7000 si se elije SP3/SP4.
'''Cuidado:''' El offset debe ser menor a $10-Cantidad de lineas de 8 pixeles de alto usadas por el sprite, por ejemplo si el espacio en la VRAM utilizado por el sprite es 4 lineas, debe elegirse C o menos.
{| class="wikitable"
|-
! Nombre !! Tamaño
|-
| DZ_SDS_Offset_Normal || Lorom: 12 - SA-1: 22
|-
| DZ_SDS_Offset_Cluster || 20
|-
| DZ_SDS_Offset_Extended || 10
|-
| DZ_SDS_Offset_OW || 16
|}
PaletteAndPage tiene el siguiente formato:
0000PPPT:
PPP Es la paleta utilizada por el sprite:
000 = Paleta 8
001 = Paleta 9
010 = Paleta A
011 = Paleta B
100 = Paleta C
101 = Paleta D
110 = Paleta E
111 = Paleta F
T es la pagina de graficos usada, 0 es SP1/SP2, 1 es SP3/SP4.
{| class="wikitable"
|-
! Nombre !! Tamaño
|-
| DZ_SDS_PaletteAndPage_Normal || Lorom: 12 - SA-1: 22
|-
| DZ_SDS_PaletteAndPage_Cluster || 20
|-
| DZ_SDS_PaletteAndPage_Extended || 10
|-
| DZ_SDS_PaletteAndPage_OW || 16
|}
Size es la cantidad de bloques de 8x8 usados por el sprite en la VRAM.
{| class="wikitable"
|-
! Nombre !! Tamaño
|-
| DZ_SDS_Size_Normal || Lorom: 12 - SA-1: 22
|-
| DZ_SDS_Size_Cluster || 20
|-
| DZ_SDS_Size_Extended || 10
|-
| DZ_SDS_Size_OW || 16
|}
Valid indica si el sprite ya envio todos sus gráficos en la VRAM, mientras es 0, el sprite no debe ejecutarse.
{| class="wikitable"
|-
! Nombre !! Tamaño
|-
| DZ_SDS_Valid_Normal || Lorom: 12 - SA-1: 22
|-
| DZ_SDS_Valid_Cluster || 20
|-
| DZ_SDS_Valid_Extended || 10
|-
| DZ_SDS_Valid_OW || 16
|}
SendOffset utilizado para realizar la rutina dinámica, este indica desde que posición debe empezar la rutina dinámica. Al inicio es igual a offset, pero si no logra enviar toda la información en un solo frame, entonces aumenta según en que posición quedo, para asi seguir enviando data en el siguiente frame.
{| class="wikitable"
|-
! Nombre !! Tamaño
|-
| DZ_SDS_SendOffset_Normal || Lorom: 12 - SA-1: 22
|-
| DZ_SDS_SendOffset_Cluster || 20
|-
| DZ_SDS_SendOffset_Extended || 10
|-
| DZ_SDS_SendOffset_OW || 16
|}
SpriteNumber es el SpriteNumber del sprite, se registra para poder saber si sigue siendo valido el slot o no.
{| class="wikitable"
|-
! Nombre !! Tamaño
|-
| DZ_SDS_SpriteNumber_Normal || Lorom: 12 - SA-1: 22
|-
| DZ_SDS_SpriteNumber_Cluster || 20
|-
| DZ_SDS_SpriteNumber_Extended || 10
|-
| DZ_SDS_SpriteNumber_OW || 16
|}
PaletteLoaded si la paleta de colores no ha sido cargada entonces es 0, sino es 1.
{| class="wikitable"
|-
! Nombre !! Tamaño
|-
| DZ_SDS_PaletteLoaded_Normal || Lorom: 12 - SA-1: 22
|-
| DZ_SDS_PaletteLoaded_Cluster || 20
|-
| DZ_SDS_PaletteLoaded_Extended || 10
|-
| DZ_SDS_PaletteLoaded_OW || 16
|}
==== Sprites Dinámicos de 2 fases ====
==== Sprites Dinámicos de 2 fases ====
==== Sprites con Rotación y Re-escalamiento ====
==== Sprites con Rotación y Re-escalamiento ====
=== Cambio de Gráficos y Tilemaps ===
=== Cambio de Gráficos y Tilemaps ===
Se utiliza una lista de transferencias hacia la VRAM. Luego de realizar las transferencias, DZ_PPUMirrors_VRAM_Transfer_Length sera 0xFF. Si DZ_PPUMirrors_VRAM_Transfer_Length es 0xFF la lista esta vacia y no se realiza ninguna transferencia.
Para utilizarlo, se recomienda usar las macros que vienen incluidos con el parche.
{| class="wikitable"
|-
! Nombre !! Tamaño !! Descripción
|-
| DZ_PPUMirrors_VRAM_Transfer_Length || 1 || Tamaño de la lista de transferencias de DMA a la VRAM. Tamaño máximo 128.
|-
| DZ_PPUMirrors_VRAM_Transfer_SourceLength || 256 || Tabla que tiene el tamaño en bytes del recurso que se enviara. 2 bytes por transferencia.
|-
| DZ_PPUMirrors_VRAM_Transfer_Offset || 256 || Tabla que tiene la Dirección de la VRAM donde llegara el recurso que se enviara. 2 bytes por transferencia.
|-
| DZ_PPUMirrors_VRAM_Transfer_Source || 256 || Tabla que tiene la Dirección del recurso que se enviara. 2 bytes por transferencia.
|-
| DZ_PPUMirrors_VRAM_Transfer_PPUSourceBNK || 256 || Tabla que tiene el BNK del recurso que se enviara. 2 bytes por transferencia (el high byte no se toma en cuenta).
|}
==== Cambio de Bloques ====
==== Cambio de Bloques ====
==== Cambio de Gráficos del Player ====
==== Cambio de Gráficos del Player ====
{| class="wikitable"
|-
! Nombre !! Tamaño !! Descripción
|-
| DZ_Player_GFX_Enable || 1 || Si es 1, realiza la rutina dinámica normal del player, si es 0, se deshabilita permitiendo hacer una rutina dinámica personalizando.
|-
| DZ_Player_GFX_Addr || 2 || Indica la dirección donde inician los graficos del player, puede modificarse para cambiar los gráficos por otro player que use el mismo esquema que los gráficos originales. Por default es 0x2000.
|-
| DZ_Player_GFX_BNK || 2 || Indica el BNK donde estan almacenados los graficos del player, puede modificarse para cambiar los gráficos por otro player que use el mismo esquema que los gráficos originales. El High byte debe ser 0x00, por default es 0x007F.
|}
=== Cambio de Paletas de colores ===
=== Cambio de Paletas de colores ===
==== Sistema de Tinte-Saturación-Valor (HSV) ====
==== Sistema de Tinte-Saturación-Valor (HSV) ====
==== Sistema de Rojo-Verde-Azul (RGB) ====
==== Sistema de Rojo-Verde-Azul (RGB) ====
==== Cambio de Paletas de colores del Player ====
==== Cambio de Paletas de colores del Player ====
{| class="wikitable"
|-
! Nombre !! Tamaño !! Descripción
|-
| DZ_Player_Palette_Enable || 1 || Si es 1, se realiza la rutina default de cambio de paleta del player. Si es 0, no se realiza esa rutina, permitiendo una personalizada.
|-
| DZ_Player_Palette_Addr || 2 || Indica la dirección de memoria donde se almacena la paleta del player, puede modificarse para cambiar los colores en la paleta de colores 8 iniciando del color 0x86. Por default tiene el valor dado por la dirección de WRAM $7E0D82.
|-
| DZ_Player_Palette_BNK || 1 || Indica el BNK donde se almacena la paleta del player, puede modificarse para cambiar los colores en la paleta de colores 8 iniciando del color #$86. Por default es 0x00.
|-
| DZ_PPUMirrors_CGRAM_LastPlayerPal || 2 || Almacena la dirección de memoria de la ultima paleta que fue cargada. Si su valor es 0xFFFF y se utiliza la rutina de cambio de paleta default, se forzara el rutina de cambio de paleta en ese frame.
|}
=== Sistema de OAM ===
=== Sistema de OAM ===
=== Modo 50% más ===  
=== Modo 50% más ===  
=== Widescreen ===
=== Widescreen ===
=== Mirror de 420B ===
 
Se utiliza una rutina de HDMA para la realizar el widescreen, luego de eso, se calcula la máxima cantidad de data que se puede enviar a la VRAM.
 
{| class="wikitable"
|-
! Nombre !! Tamaño !! Descripción
|-
| DZ_PPUMirrors_WS_Enable || 1 || Si es 0, no se realiza la rutina de HDMA, si es 1 se realiza.
|-
| DZ_PPUMirrors_WS_Buffer || 435 || Un buffer que tiene 2 bytes por scanline, el primer byte de cada scanline tiene el alto de la scanline y el segundo es el valor del registro 2100, al final del buffer se pone el valor 0xFF. Este buffer debe ser escrito usando las macros que vienen con el parche.
|}
 
=== Otros ===
=== Otros ===
== Macros ==
== Macros ==
=== Generales ===
=== Generales ===
==== Mul(a, b) ====
Multiplica dos números de 8 bits.
===== Parametros =====
* a: multiplicando 1.
* b: multiplicando 2.
===== Retorno =====
El resultado queda almacenado en !MultiplicationResult, Se debe esperar 8 cyclos antes de leer el resultado (Solo 5 si se usa SA-1).
==== MulAfterMul(b) ====
Multiplica un número de 8 bits con el resultado de una multiplicación anterior.
===== Parametros =====
* b: Número a multiplicar
===== Retorno =====
El resultado queda almacenado en !MultiplicationResult, Se debe esperar 8 cyclos antes de leer el resultado (Solo 5 si se usa SA-1).
==== MulAfterDiv(b) ====
Multiplica un número de 8 bits con el resultado de una división anterior.
===== Parametros =====
* b: Número a multiplicar
===== Retorno =====
El resultado queda almacenado en !MultiplicationResult, Se debe esperar 8 cyclos antes de leer el resultado (Solo 5 si se usa SA-1).
==== MulW(a, b) ====
Similar a Mul pero no requiere esperar antes de leer el resultado.
==== MulWAfterMul(b) ====
Similar a MulAfterMul pero no requiere esperar antes de leer el resultado.
==== MulWAfterDiv(b) ====
Similar a MulAfterDiv pero no requiere esperar antes de leer el resultado.
==== Div(ah, al, b) ====
Divide un número de 16 bits por un número de 8 bits.
===== Parametros =====
* ah: High byte del dividendo.
* al: High byte del dividendo.
* b: Divisor.
===== Retorno =====
El resultado queda almacenado en !DivisionResult, se deben esperar 16 ciclos (5 si se usa SA-1).
==== DivAfterMul(b) ====
Divide el resultado de una multiplicación anterior por un número de 8 bits.
===== Parametros =====
* b: Divisor.
===== Retorno =====
El resultado queda almacenado en !DivisionResult, se deben esperar 16 ciclos (5 si se usa SA-1).
==== DivAfterDiv(b) ====
Divide el resultado de una división anterior por un número de 8 bits.
===== Parametros =====
* b: Divisor.
===== Retorno =====
El resultado queda almacenado en !DivisionResult, se deben esperar 16 ciclos (5 si se usa SA-1).
==== DivW(ah, al, b) ====
Similar a Div pero no requiere esperar antes de leer el resultado.
==== DivWAfterMul(b) ====
Similar a DivAfterMul pero no requiere esperar antes de leer el resultado.
==== DivWAfterDiv(b) ====
Similar a DivAfterDiv pero no requiere esperar antes de leer el resultado.
=== Sprites Dinámicos ===
=== Sprites Dinámicos ===
==== FindSpace(DSSlotUsed) ====
Se utiliza para que el Sprite Dinámico busque un espacio libre más adecuado para poner los gráficos y asi se defragmente la VRAM.
===== Parametros =====
Recibe una de las direcciones de tipo "DZ_DS_Loc_US_XXXX", debe usarse la que corresponde al tipo de sprite, por ejemplo, si se utiliza en un sprite normal debe usarse "DZ_DS_Loc_US_Normal,x"
===== Retorno =====
Si retorna Carry Clear el sprite no encontro ninguna posición valida sus gráficos, si el sprite no es Valido, debe ser vuelto a cargar, Si retorna Carry Set se puede realizar la rutina dinamica.
Si la dirección $0B luego de usar esta macro no es 0x00, entonces debe forzarse la rutina dinámica en este frame.
==== CheckSlot(FrameRateMode, NumberOf16x16Tiles, SpriteNumber, SpriteTypeAndSlot, SpriteUsedSlot) ====
Remueve de la lista los Sprites Dinámicos que ya no esten vivos o sean invalidos, además busca un espacio en la lista de sprites dinámicos para un sprite nuevo. Solo debe ser usada para Cluster, Extended u OW sprites, para sprites normales usar CheckSlotNormalSprite.
===== Parametros =====
* FrameRateMode: Indica en que frames puede enviar Gráficos a la VRAM.
** 00: Se le asigna automaticamente de forma balanceada.
* NumberOf16x16Tiles: Cantidad de tiles de 16x16 utilizados por el Sprite Dinámico.
* SpriteNumber: Dirección que almacena el Sprite Number del sprite dinámico, por ejemplo, en el caso de los clusters seria "$1892|!Addr,x".
* SpriteTypeAndSlot: Indica de que tipo de sprite es y si es un sprite usa o no memoria compartida. tiene el siguiente formato:
** STT0 0000
** S: Si usa memoria compartida debe ser 1, sino es 0.
** TT: El tipo de sprite, 00 es Normal, 01 es Cluster, 10 es Extended y 11 es OW.
SpriteUsedSlot: Debe usarse una de las direcciones DZ_DS_Loc_US_XXXX segun corresponda, por ejemplo, para Cluster  seria DZ_DS_Loc_US_Cluster.
===== Retorno =====
Si no hay espacio disponible en la VRAM, la lista de sprites dinámicos esta llena o si se excede la cantidad máxima de data que se puede enviar en el frame, el sprite sera eliminado.
==== CheckSlotNormalSprite(NumberOf16x16Tiles, SpriteTypeAndSlot) ====
Similar a CheckSlot pero para Sprites Dinámicos.
===== Parametros =====
* NumberOf16x16Tiles: Cantidad de tiles de 16x16 utilizados el sprite dinámico.
* SpriteTypeAndSlot: Si el sprite usa memoria compartida, debe ser 0x80, sino debe ser 0x00.
===== Retorno =====
Si no hay espacio disponible en la VRAM, la lista de sprites dinámicos esta llena o si se excede la cantidad máxima de data que se puede enviar en el frame, el sprite sera eliminado y se podre ser respawneado.
==== DynamicRoutine(VRAMOffset, ResourceAddr, ResourceBNK, ResourceOffset, size) ====
Establece los mirros de VRAM necesarios para que el sprite envie la información a la PPU. Solo puede enviar memoria continua a la VRAM, por lo que si el frame usa memoria discontinua en la VRAM o en el recurso, debe llamarse más de una vez con distintos parametros.
===== Parametros =====
* VRAMOffset: Indica a que posición de la VRAM llegaran los graficos, debe estar en Tiles de 8x8. La data llegara a la Dirección de VRAM DZ_DS_StartingVRAMOffset + (0x10*VRAMOffset). Se puede conseguir usando la Macro GetVramDispDynamicRoutine(DSLocUS).
* ResourceAddr: La dirección de memoria donde estan almacenados los gráficos del sprite.
* ResourceBNK: El BNK donde estan almacenados los gráficos del sprite.
* ResourceOffset: La posición donde inicia el frame que debe enviarse a la VRAM dentro del GFX.
* size: Cantidad de data en tiles de 8x8 que deben enviarse a la VRAM.
==== GFXTabDef(index) ====
Utilizado por Dynamic Resource Adder para identificar donde están almacenadas las direcciones de los recursos usados por el el código, al usarlo genera el Define !GraphicsTable que tiene la dirección de memoria donde esta guardada la tabla que especifica donde estan guardados los recursos dinámicos usados por el código.
==== GFXDef(offset) ====
Utilizado por Dynamic Resource Adder para identificar donde esta almacenado uno de los recursos usados por el código, al usarlo genera el Define !GFXZZ donde ZZ es es un numero en hexadecimal que es el ID del recurso (Ejemplo: 00, 01, etc.) y que tiene la dirección de memoria donde esta guardado el recurso dinámico.
==== CheckEvenOrOdd(DSLocUS) ====
Utilizado para saber si un sprite dinámico de 30FPS debe cambiar de frame o debe realizar su rutina dinámica.
===== Parametros =====
*DSLocUS: Debe ser una de las direcciones "DZ_DS_Loc_US_XXXX" por ejemplo: "DZ_DS_Loc_US_Normal".
===== Retorno =====
Si despues de usar esta rutina el Flag N es Clear entonces puede realizar su rutina dinámica, sino puede hacer cambio de Frame.
==== GetVramDisp(DSLocUS) ====
Utilizado para obtener el Offset desde donde se deben empezar a realizar la rutina Gráfica del sprite.
===== Parametros =====
*DSLocUS: Debe ser una de las direcciones "DZ_DS_Loc_US_XXXX" por ejemplo: "DZ_DS_Loc_US_Normal".
===== Retorno =====
A = Offset para los tiles de la rutina gráfica.
==== GetVramDispDynamicRoutine(DSLocUS) ====
Utilizado para obtener el Offset desde donde se deben llegar los gráficos en la VRAM en la rutina Dinámica del sprite.
===== Parametros =====
*DSLocUS: Debe ser una de las direcciones "DZ_DS_Loc_US_XXXX" por ejemplo: "DZ_DS_Loc_US_Normal".
===== Retorno =====
A = Offset para la data de la rutina dinámica.
==== RemapOamTile(Tile, Offset) ====
Utilizado para remappear los tiles en la rutina Gráfica.
===== Parametros =====
* Tile: Tile que debe remappearse.
* Offset: Resultado obtenido de %GetVramDisp(DZ_DS_Loc_US_Normal).
==== Sprites Dinámicos con Memoria Compartida ====
==== Sprites Dinámicos con Memoria Compartida ====
==== CheckSlotNormalSpriteNoRemove(NumberOf16x16Tiles, SpriteTypeAndSlot) ====
Similar a CheckSlotNormalSprite pero no elimina el sprite si falla.
===== Parametros =====
* NumberOf16x16Tiles: Cantidad de tiles de 16x16 utilizados el sprite dinámico.
* SpriteTypeAndSlot: Si el sprite usa memoria compartida, debe ser 0x80, sino debe ser 0x00.
===== Retorno =====
Si no hay espacio disponible en la VRAM, la lista de sprites dinámicos esta llena o si se excede la cantidad máxima de data que se puede enviar en el frame, retorna Carry Clear, sino Carry Set.
==== Sprites Semi-Dinámicos ====
==== Sprites Semi-Dinámicos ====
==== Sprites Dinámicos de 2 fases ====
==== Sprites Dinámicos de 2 fases ====
==== Sprites con Rotación y Re-escalamiento ====
==== Sprites con Rotación y Re-escalamiento ====
=== Cambio de Gráficos y Tilemaps ===
=== Cambio de Gráficos y Tilemaps ===
==== ForcedTransferToVRAM(VRAMOffset, ResourceAddr, ResourceBNK, Lenght) ====
Setea la información necesaria en los Mirrors de VRAM, para enviar gráficos a la PPU.
===== Parametros =====
* VRAMOffset: Dirección de VRAM a la que llegara la data.
* ResourceAddr: Dirección del recurso que se enviara a la VRAM.
* ResourceBNK: BNK del recurso que se enviara a la VRAM. Debe ser de 2 Bytes, el highbyte debe ser 0x00.
* Lenght: Cantidad de información que se enviara a la VRAM.
==== TransferToVRAM(VRAMOffset, ResourceAddr, ResourceBNK, Lenght) ====
Similar a ForcedTransferToVRAM pero no produce Flickering. Puede fallar si se excede la cantidad máxima de información a enviar durante el NMI Handler.
===== Parametros =====
* VRAMOffset: Dirección de VRAM a la que llegara la data.
* ResourceAddr: Dirección del recurso que se enviara a la VRAM.
* ResourceBNK: BNK del recurso que se enviara a la VRAM. Debe ser de 2 Bytes, el highbyte debe ser 0x00.
* Lenght: Cantidad de información que se enviara a la VRAM.
===== Retorno =====
Si el Carry es Set, entonces se pudo realizar la rutina, sino Fallo.
==== Cambio de Bloques ====
==== Cambio de Bloques ====
==== Cambio de Gráficos del Player ====
==== Cambio de Gráficos del Player ====
=== Cambio de Paletas de colores ===
=== Cambio de Paletas de colores ===
==== ForcedTransferToCGRAM(CGRAMOffset, ResourceAddr, ResourceBNK, Lenght) ====
Setea la información necesaria en los Mirrors de CGRAM, para enviar gráficos a la PPU.
===== Parametros =====
* CGRAMOffset: Dirección de CGRAM a la que llegara la data.
* TableAddr: Dirección del recurso que se enviara a la CGRAM.
* TableBNK: BNK del recurso que se enviara a la CGRAM.
* Lenght: Cantidad de información que se enviara a la CGRAM.
==== TransferToCGRAM(CGRAMOffset, ResourceAddr, ResourceBNK, Lenght) ====
Similar a ForcedTransferToCGRAM pero no produce Flickering. Puede fallar si se excede la cantidad máxima de información a enviar durante el NMI Handler.
===== Parametros =====
* CGRAMOffset: Dirección de CGRAM a la que llegara la data.
* TableAddr: Dirección del recurso que se enviara a la CGRAM.
* TableBNK: BNK del recurso que se enviara a la CGRAM.
* Lenght: Cantidad de información que se enviara a la CGRAM.
===== Retorno =====
Si el Carry es Set, entonces se pudo realizar la rutina, sino Fallo.
==== Sistema de Tinte-Saturación-Valor (HSV) ====
==== Sistema de Tinte-Saturación-Valor (HSV) ====
==== Sistema de Rojo-Verde-Azul (RGB) ====
==== Sistema de Rojo-Verde-Azul (RGB) ====
==== Cambio de Paletas de colores del Player ====
==== Cambio de Paletas de colores del Player ====
=== Sistema de OAM ===
=== Sistema de OAM ===
=== Modo 50% más ===  
=== Modo 50% más ===  
=== Widescreen ===
=== Widescreen ===
=== Mirror de 420B ===
=== Otros ===
=== Otros ===
== Rutinas ==
== Rutinas ==
Line 151: Line 743:
=== Modo 50% más ===  
=== Modo 50% más ===  
=== Widescreen ===
=== Widescreen ===
=== Mirror de 420B ===
=== Otros ===
=== Otros ===


== Sprites Dinámicos ==
== Sprites Dinámicos ==
Un Sprite Dinámico es un Sprite que sus gráficos se cargan en la VRAM a medida que son requeridos. Existen distintos tipos de Sprite dinámico:
* Sprites Dinámicos Con Memoria Compartida: Es un Sprite Dinámico en que todas las copias de él, comparten el mismo cuadro de animación en todo momento.
* Sprites Dinámicos Con Memoria Mixta: Es un Sprite Dinámico que puede usar memoria compartida solo para ciertas animaciones.
* Sprites Semi-Dinámicos: Es un Sprite que carga en la VRAM todos los gráficos que necesita cuando es utilizado, luego todas las copias de dicho sprite, utilizan el mismo espacio en la VRAM para los gráficos.
* Sprites Dinámicos de 2 fases: Es un Sprite Dinámico pero cada frame requiere 2 loops para ser cargado en la VRAM, por lo tanto, la velocidad de animación máxima es 30FPS. Estos sprites suelen ser de gran tamaño y están pensados para la realización de jefes.
* Sprites con Rotación y Re-escalamiento: Es un Sprite Dinámico pero que al empezar el nivel, calcula sus frames con distintos ángulos de rotación o distintos tamaños y luego los utiliza para sus animaciones. Se recomienda siempre dentro de lo posible, no utilizarlos, ya que, el algoritmo de rotación y escalamiento utiliza vecinos más cercanos y los cuadros de animación terminan con mala calidad, además, requieren un Setup usando uberasm, se recomienda redibujar cada frame a mano o hacer sus gráficos con un Tool Externo, solo se recomienda utilizar en caso que se tenga poco espacio en el ROM.


=== Uso ===
=== Uso ===
# Usando el Dynamic Resource Adder (DRAdder), agrega todos los recursos a la carpeta Dynamic Resources.
# Agrega el nombre del sprite en la lista "ResourceList.txt" en la zona que corresponda.
# Inserta los recursos con el DRAdder e inserta el sprite con pixi.
==== Uso de Sprites Dinámicos (Con o sin Memoria compartida) ====
El primer extra byte tiene el siguiente formato:
TT?? ????
TT = Asigna si el sprite usa solo frames impares o pares para enviar sus graficos a la VRAM. 00 = Asigna de forma automatica asignando de forma balanceada, 01 = Solo envia en frames pares, 10 = Envia en solo frames impares, 11 = No usado.
==== Uso de Sprites Semi-Dinámicos ====
El primer extra byte tiene el siguiente formato:
GGGG PPP T
* GGGG = Asigna en que linea de 128x8 de la VRAM se enviaran los grafico donde 0 es el tope y F es el fondo.
* PPP = Paleta que usara el sprite
** 000 = Paleta 8
** 001 = Paleta 9
** 010 = Paleta A
** 011 = Paleta B
** 100 = Paleta C
** 101 = Paleta D
** 110 = Paleta E
** 111 = Paleta F
* T = Si es 0 Usa SP1 y SP2, Si es 1 Usa SP3 y SP4.


=== Creación de Sprites Dinámicos ===
=== Creación de Sprites Dinámicos ===
Para crear un sprite dinámico sigue los siguientes pasos:
<ol start="1">
<li>Define una ram que usaras para almacenar el ultimo cuadro de animación que utilizo el sprite y otra para el cuadro de animación actual. Ejemplo:
<pre>
!LastFrameIndex = !SpriteMiscTable6
!FrameIndex = !SpriteMiscTable1
</pre>
</li>
<li>En el Sprite Init has que inicie !LastFrameIndex en 0xFF, !FrameIndex ponle el valor del primer frame que se mostrara (yo le pondre 0).
<pre>
LDA #$FF
STA !LastFrameIndex,x
        STZ !FrameIndex,x
</pre></li>
<li>Llama en el sprite init, la macro CheckSlotNormalSprite si es un sprite normal o CheckSlot si es Cluster, Extended u OW Sprite.
Normal:
<pre>
%CheckSlotNormalSprite(#$0C, $00)
</pre>
El primer parametro es la cantidad de tiles de 16x16 que usa el sprite como máximo y el segundo parametro debe ser $00. Si el sprite es de 60FPS, setea el Extra Byte 1 en #$E0 antes de llamarla.
Otros:
<pre>
%CheckSlot(#$00, #$01, "!ClusterNumber,x", $20, DZ_DS_Loc_US_Cluster)
</pre>
El primer parametro normalmente es #$00, pero podrias ponerle #$01 si el sprite solo envia graficos en frames pares, #$02 si quieres que solo envie graficos en frames impares o #$03 si es un sprite de 60FPS.
El segundo parametro es la cantidad de tiles de 16x16 que usa el sprite.
Los otros parametros dependen del tipo:
Cluster: "!ClusterSpriteNumber,x", $20, DZ_DS_Loc_US_Cluster
Extended: "!ExtendedSpriteNumber,x", $40, DZ_DS_Loc_US_Extended
OW: "!OWSpriteNumber,x", $60, DZ_DS_Loc_US_OW
</li>
<li>Luego al inicio del SpriteCode antes de llamar a la rutina gráfica, añade esto:
<pre>
JSR DynamicRoutine
LDA DZ_DS_Loc_US_Normal,x
TAX
LDA DZ_DS_Loc_IsValid,x
BNE +
LDX !SpriteIndex
RTS
+
LDX !SpriteIndex
</pre>
La primera linea llama a la rutina dinamica, las demás chequean si el sprite es valido, si el sprite es invalido el sprite no se ejecuta. El sprite es invalido si no ha podido realizar su rutina dinámica aun. DZ_DS_Loc_US_Normal Depende del tipo de sprite, Normal es para sprites normales, Cluster para clusters y asi respectivamente.
</li>
<li>Para generar la rutina Dinámico iremos paso a paso. Lo primero es tener el GFX, para esto puedes utilizar Dyzen que generara todas las tablas por ti, sino debes generar el GFX por ti mismo, para esto lo importante es que cada cuadro de animación use memoria continua, ejemplo:
<gallery>
https://i.gyazo.com/bab0672675b3fc519104d05e730a7741.png|Ejemplo de Frame
</gallery>
Una vez que tenemos todos los frames hechos, debemos hacer las tablas, necesitamos 2 tablas ResourceOffset y ResourceSize.
* ResourceOffset: Cada valor es de 16 bits (dw), por cada cuadro de animación necesita 2 valores, el primero es la posición en que esta guardado el cuadro de animación, el segundo es donde inicia la ultima linea de 8x8 usada por el sprite. Ejemplo si asi debe quedar guardado el cuadro de animación en la VRAM.
<gallery>
https://i.gyazo.com/329c07c084e3e9c83c80d73879d4e42b.png|Ejemplo en VRAM
</gallery>
Y el GFX luce asi:
<gallery>
https://i.gyazo.com/bab0672675b3fc519104d05e730a7741.png|Ejemplo de Frame
</gallery>
El primer valor seria $0000, ya que el cuadro de animación esta al inicio del GFX, mientras que la segunda linea esta a 35 tiles de 8x8 del inicio del GFX, cada tile de 8x8 usa 0x20 bytes, por lo tanto el segundo valor sera $0460
Si tuviera un segundo frame por ejemplo:
<gallery>
https://i.gyazo.com/7e7d92b81f7b76ac18daa30d4eae7f86.png|Frame 2
</gallery>
Y debe verse asi en la VRAM:
<gallery>
https://i.gyazo.com/74fbb1ce3c278459f32858a3cb2928b0.png|Frame 2 VRAM
</gallery>
El segundo frame parte en la posición final del primer frame ($0460) más la cantidad de tiles de 8x8 usadas por la segunda linea del primer frame (2 tiles), por lo tanto, el primer valor seria $04A0, la segunda linea seria estaria a 36 tiles desde esa posición por lo que seria 36*0x20 = $0920.
* ResourceSize: Similar a ResourceOffset, tambien usa 2 valores, pero son de 8 bitx (db), Cada valor indica cuantos tiles de 8x8 necesita cada transferencia, por lo tanto, siguiendo con el ejemplo anterior, el primer cuadro de animación requiere 2 transferencia, uno de 35 tiles (0x23) y otra de 2 tiles, por lo tanto, el primer valor seria $23 y el segundo $02. Para el segundo frame seria uno de 36 tiles (0x24) y otra de 2 tiles, por lo tanto seria $24 y $02. '''Importante: No siempre se necesita poner la ultima linea, Si el frame completo usa solo tiles de 16x16, el segundo valor debe ser 0.'''
Aqui un ejemplo de las tablas de Klump:
<pre>
ResourceOffset:
Walk0_ResourceOffset:
dw $0000,$0460
Walk1_ResourceOffset:
dw $04A0,$0920
Walk2_ResourceOffset:
dw $0960,$0E00
Walk3_ResourceOffset:
dw $0E80,$1300
Walk4_ResourceOffset:
dw $1380,$1800
Walk5_ResourceOffset:
dw $1880,$1CE0
Walk6_ResourceOffset:
dw $1CE0,$2140
Walk7_ResourceOffset:
dw $2180,$2620
Flip0_ResourceOffset:
dw $26A0,$2B00
Resist0_ResourceOffset:
dw $2B40,$2FC0
Death0_ResourceOffset:
dw $3000,$34E0
Death1_ResourceOffset:
dw $35A0,$3A40
Death2_ResourceOffset:
dw $3AC0,$3F80
Death3_ResourceOffset:
dw $4040,$4500
Death4_ResourceOffset:
dw $45C0,$49E0
Death5_ResourceOffset:
dw $49E0,$4DE0
Death6_ResourceOffset:
dw $4DE0,$51E0
ResourceSize:
Walk0_ResourceSize:
db $23,$02
Walk1_ResourceSize:
db $24,$02
Walk2_ResourceSize:
db $25,$04
Walk3_ResourceSize:
db $24,$04
Walk4_ResourceSize:
db $24,$04
Walk5_ResourceSize:
db $23,$00
Walk6_ResourceSize:
db $23,$02
Walk7_ResourceSize:
db $25,$04
Flip0_ResourceSize:
db $23,$02
Resist0_ResourceSize:
db $24,$02
Death0_ResourceSize:
db $27,$06
Death1_ResourceSize:
db $25,$04
Death2_ResourceSize:
db $26,$06
Death3_ResourceSize:
db $26,$06
Death4_ResourceSize:
db $21,$00
Death5_ResourceSize:
db $20,$00
Death6_ResourceSize:
db $20,$00
</pre>
</li>
<li>Crea una rutina llamada DynamicRoutine.</li>
<pre>
DynamicRoutine:
RTS
</pre>
Si usas la macro %EasyDynamicRoutine, puedes saltarte los pasos siguientes, sin embargo igual se explicaran debido a que puede que necesites hacer una rutina dinámica más flexible. Si usas %EasyDynamicRoutine, solamente necesitas lo siguiente:
WIP
<li>Primero se hacen los chequeos para saber si el sprite puede o no realizar la rutina gráfica, el primero chequeo es si el el frame actual concuerda con el tipo de frame en que tiene permitido el sprite dinámico realizar su rutina. Para esto usamos:
<pre>
DynamicRoutine:
%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
BEQ +
RTS
+
RTS
</pre>
"DZ_DS_Loc_US_Normal" Cambia segun el tipo de sprite. '''Este chequeo no es necesario si el sprite es de 60FPS.'''
</li>
<li>El segundo chequeo se realiza con la macro %FindSpace, Si retorna Carry Clear y el sprite es invalido debemos eliminar el sprite, si es valido se debe salir de la rutina dinámica, si el Carry es Set puede realizar su rutina dinámica y si $0B es distinto de 0, debe forzarse la rutina dinámica saltandose los chequeos siguientes.
Normal:
<pre>
DynamicRoutine:
%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
BEQ + ;/
RTS
+
%FindSpace("DZ_DS_Loc_US_Normal,x")
BCS +
LDA.l DZ_DS_Loc_US_Normal,x
TAX
LDA.l DZ_DS_Loc_IsValid,x
BNE ++
        LDX $15E9|!addr
        STZ !SpriteStatus,x
        LDA !SpriteLoadStatus,x
        TAX
        LDA #$00
        STA !SpriteLoadTable,x
++
        LDX $15E9|!addr
RTS
+
LDA !ScratchB
BNE +
+
RTS
</pre>
Cluster:
<pre>
DynamicRoutine:
%CheckEvenOrOdd("DZ_DS_Loc_US_Cluster")
BEQ + ;/
RTS
+
%FindSpace("DZ_DS_Loc_US_Cluster,x")
BCS +
        PHX
LDA.l DZ_DS_Loc_US_Cluster,x
TAX
LDA.l DZ_DS_Loc_IsValid,x
BNE ++
        STZ !ClusterSpriteNumber,x
++
        PLX
RTS
+
LDA !ScratchB
BNE +
+
RTS
</pre>
</li>
<li>El siguiente chequeo es para verificar que no se envie un frame que ya esta cargado en la VRAM. Para esto se usa las tablas que definimos en el paso 1:
Normal:
<pre>
DynamicRoutine:
%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
BEQ + ;/
RTS
+
%FindSpace("DZ_DS_Loc_US_Normal,x")
BCS +
LDA.l DZ_DS_Loc_US_Normal,x
TAX
LDA.l DZ_DS_Loc_IsValid,x
BNE ++
        LDX $15E9|!addr
        STZ !SpriteStatus,x
        LDA !SpriteLoadStatus,x
        TAX
        LDA #$00
        STA !SpriteLoadTable,x
++
        LDX $15E9|!addr
RTS
+
LDA !ScratchB
BNE +
LDA !FrameIndex,x ;\
CMP !LastFrameIndex,x ;|if last frame is different to new frame then
BNE + ;|do dynamic routine
RTS ;/
+
RTS
</pre>
Cluster:
<pre>
DynamicRoutine:
%CheckEvenOrOdd("DZ_DS_Loc_US_Cluster")
BEQ + ;/
RTS
+
%FindSpace("DZ_DS_Loc_US_Cluster,x")
BCS +
        PHX
LDA.l DZ_DS_Loc_US_Cluster,x
TAX
LDA.l DZ_DS_Loc_IsValid,x
BNE ++
        STZ !ClusterSpriteNumber,x
++
        PLX
RTS
+
LDA !ScratchB
BNE +
LDA !FrameIndex,x ;\
CMP !LastFrameIndex,x ;|if last frame is different to new frame then
BNE + ;|do dynamic routine
RTS ;/
+
RTS
</pre>
</li>
<li>Lo siguiente es rellenar los parametros para llamar a la macro %DynamicRoutine, Para esto nos ayudaremos de algunas Scratch RAM, lo primero es rellenar el offset y el size:
Normal:
<pre>
DynamicRoutine:
%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
BEQ + ;/
RTS
+
%FindSpace("DZ_DS_Loc_US_Normal,x")
BCS +
LDA.l DZ_DS_Loc_US_Normal,x
TAX
LDA.l DZ_DS_Loc_IsValid,x
BNE ++
        LDX $15E9|!addr
        STZ !SpriteStatus,x
        LDA !SpriteLoadStatus,x
        TAX
        LDA #$00
        STA !SpriteLoadTable,x
++
        LDX $15E9|!addr
RTS
+
LDA !ScratchB
BNE +
LDA !FrameIndex,x ;\
CMP !LastFrameIndex,x ;|if last frame is different to new frame then
BNE + ;|do dynamic routine
RTS ;/
+
LDA #$00
XBA
LDA !FrameIndex,x
REP #$30
ASL
TAY
PHY
SEP #$20
LDA ResourceSize,y
STA !Scratch0
REP #$20
TYA
ASL
TAY
PHY
LDA ResourceOffset,y
STA !Scratch1
SEP #$30
RTS
</pre>
Cluster:
<pre>
DynamicRoutine:
%CheckEvenOrOdd("DZ_DS_Loc_US_Cluster")
BEQ + ;/
RTS
+
%FindSpace("DZ_DS_Loc_US_Cluster,x")
BCS +
        PHX
LDA.l DZ_DS_Loc_US_Cluster,x
TAX
LDA.l DZ_DS_Loc_IsValid,x
BNE ++
        STZ !ClusterSpriteNumber,x
++
        PLX
RTS
+
LDA !ScratchB
BNE +
LDA !FrameIndex,x ;\
CMP !LastFrameIndex,x ;|if last frame is different to new frame then
BNE + ;|do dynamic routine
RTS ;/
+
        PHX
LDA #$00
XBA
LDA !FrameIndex,x
REP #$30
ASL
TAY
PHY
SEP #$20
LDA ResourceSize,y
STA !Scratch0
REP #$20
TYA
ASL
TAY
PHY
LDA ResourceOffset,y
STA !Scratch1
SEP #$30
RTS
</pre>
En este caso agregue además los PHY para usar esos valores despues y ahorrar algunos ciclos.
</li>
<li>Actualizamos el !LastFrameIndex, conseguimos el offset, ponemos al sprite como valido y llamamos la macro %DynamicRoutine, apoyandono en los Defines generados por el DRAdder.
Normal:
<pre>
DynamicRoutine:
%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
BEQ + ;/
RTS
+
%FindSpace("DZ_DS_Loc_US_Normal,x")
BCS +
LDA.l DZ_DS_Loc_US_Normal,x
TAX
LDA.l DZ_DS_Loc_IsValid,x
BNE ++
        LDX $15E9|!addr
        STZ !SpriteStatus,x
        LDA !SpriteLoadStatus,x
        TAX
        LDA #$00
        STA !SpriteLoadTable,x
++
        LDX $15E9|!addr
RTS
+
LDA !ScratchB
BNE +
LDA !FrameIndex,x ;\
CMP !LastFrameIndex,x ;|if last frame is different to new frame then
BNE + ;|do dynamic routine
RTS ;/
+
LDA #$00
XBA
LDA !FrameIndex,x
REP #$30
ASL
TAY
PHY
SEP #$20
LDA ResourceSize,y
STA !Scratch0
REP #$20
TYA
ASL
TAY
PHY
LDA ResourceOffset,y
STA !Scratch1
SEP #$30
LDA !FrameIndex,x
STA !LastFrameIndex,x
TAY
%GetVramDispDynamicRoutine(DZ_DS_Loc_US_Normal)
STA !ScratchD
LDA.l DZ_DS_Loc_US_Normal,x
TAX
LDA #$01
STA.l DZ_DS_Loc_IsValid,x
%DynamicRoutine(!ScratchD, #!GFX00, #!GFX00>>16, !Scratch1, !Scratch0)
RTS
</pre>
Cluster:
<pre>
DynamicRoutine:
%CheckEvenOrOdd("DZ_DS_Loc_US_Cluster")
BEQ + ;/
RTS
+
%FindSpace("DZ_DS_Loc_US_Cluster,x")
BCS +
        PHX
LDA.l DZ_DS_Loc_US_Cluster,x
TAX
LDA.l DZ_DS_Loc_IsValid,x
BNE ++
        STZ !ClusterSpriteNumber,x
++
        PLX
RTS
+
LDA !ScratchB
BNE +
LDA !FrameIndex,x ;\
CMP !LastFrameIndex,x ;|if last frame is different to new frame then
BNE + ;|do dynamic routine
RTS ;/
+
        PHX
LDA #$00
XBA
LDA !FrameIndex,x
REP #$30
ASL
TAY
PHY
SEP #$20
LDA ResourceSize,y
STA !Scratch0
REP #$20
TYA
ASL
TAY
PHY
LDA ResourceOffset,y
STA !Scratch1
SEP #$30
LDA !FrameIndex,x
STA !LastFrameIndex,x
TAY
%GetVramDispDynamicRoutine(DZ_DS_Loc_US_Cluster)
STA !ScratchD
LDA.l DZ_DS_Loc_US_Cluster,x
TAX
LDA #$01
STA.l DZ_DS_Loc_IsValid,x
%DynamicRoutine(!ScratchD, #!GFX00, #!GFX00>>16, !Scratch1, !Scratch0)
RTS
</pre>
</li>
<li>Ahora se debe hacer lo mismo con la ultima linea, pero varios de esos valores, ya los tenemos, ya que, se usaron en la llamada anterior.
Normal:
<pre>
DynamicRoutine:
%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
BEQ + ;/
RTS
+
%FindSpace("DZ_DS_Loc_US_Normal,x")
BCS +
LDA.l DZ_DS_Loc_US_Normal,x
TAX
LDA.l DZ_DS_Loc_IsValid,x
BNE ++
        LDX $15E9|!addr
        STZ !SpriteStatus,x
        LDA !SpriteLoadStatus,x
        TAX
        LDA #$00
        STA !SpriteLoadTable,x
++
        LDX $15E9|!addr
RTS
+
LDA !ScratchB
BNE +
LDA !FrameIndex,x ;\
CMP !LastFrameIndex,x ;|if last frame is different to new frame then
BNE + ;|do dynamic routine
RTS ;/
+
LDA #$00
XBA
LDA !FrameIndex,x
REP #$30
ASL
TAY
PHY
SEP #$20
LDA ResourceSize,y
STA !Scratch0
REP #$20
TYA
ASL
TAY
PHY
LDA ResourceOffset,y
STA !Scratch1
SEP #$30
LDA !FrameIndex,x
STA !LastFrameIndex,x
TAY
%GetVramDispDynamicRoutine(DZ_DS_Loc_US_Normal)
STA !ScratchD
LDA.l DZ_DS_Loc_US_Normal,x
TAX
LDA #$01
STA.l DZ_DS_Loc_IsValid,x
%DynamicRoutine(!ScratchD, #!GFX00, #!GFX00>>16, !Scratch1, !Scratch0)
REP #$30
PLY
LDA ResourceOffset+2,y
STA !Scratch1
PLY
SEP #$20
LDA ResourceSize+1,y
STA !Scratch0
SEP #$10
RTS
</pre>
Cluster:
<pre>
DynamicRoutine:
%CheckEvenOrOdd("DZ_DS_Loc_US_Cluster")
BEQ + ;/
RTS
+
%FindSpace("DZ_DS_Loc_US_Cluster,x")
BCS +
        PHX
LDA.l DZ_DS_Loc_US_Cluster,x
TAX
LDA.l DZ_DS_Loc_IsValid,x
BNE ++
        STZ !ClusterSpriteNumber,x
++
        PLX
RTS
+
LDA !ScratchB
BNE +
LDA !FrameIndex,x ;\
CMP !LastFrameIndex,x ;|if last frame is different to new frame then
BNE + ;|do dynamic routine
RTS ;/
+
        PHX
LDA #$00
XBA
LDA !FrameIndex,x
REP #$30
ASL
TAY
PHY
SEP #$20
LDA ResourceSize,y
STA !Scratch0
REP #$20
TYA
ASL
TAY
PHY
LDA ResourceOffset,y
STA !Scratch1
SEP #$30
LDA !FrameIndex,x
STA !LastFrameIndex,x
TAY
%GetVramDispDynamicRoutine(DZ_DS_Loc_US_Cluster)
STA !ScratchD
LDA.l DZ_DS_Loc_US_Cluster,x
TAX
LDA #$01
STA.l DZ_DS_Loc_IsValid,x
%DynamicRoutine(!ScratchD, #!GFX00, #!GFX00>>16, !Scratch1, !Scratch0)
REP #$30
PLY
LDA ResourceOffset+2,y
STA !Scratch1
PLY
SEP #$20
LDA ResourceSize+1,y
STA !Scratch0
SEP #$10
RTS
</pre>
</li>
<li>El VRAMDisp, debe sumarsele 0x10 por cada linea que tenga el sprite - 1, por ejemplo si el sprite usa 2 lineas de 8 pixeles de alto, entonces el valor seria 0x10, si usara 4, seria 0x30, si usara 6 seria 0x50, etc. En mi caso Klump usa 4 lineas. Luego de esto se llama a la macro %DynamicRoutine. '''Importante: si la segunda linea tiene Size igual a 0, entonces no debe realizarse la rutina, para esto añadimos un chequeo.'''
Normal:
<pre>
DynamicRoutine:
%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
BEQ + ;/
RTS
+
%FindSpace("DZ_DS_Loc_US_Normal,x")
BCS +
LDA.l DZ_DS_Loc_US_Normal,x
TAX
LDA.l DZ_DS_Loc_IsValid,x
BNE ++
        LDX $15E9|!addr
        STZ !SpriteStatus,x
        LDA !SpriteLoadStatus,x
        TAX
        LDA #$00
        STA !SpriteLoadTable,x
++
        LDX $15E9|!addr
RTS
+
LDA !ScratchB
BNE +
LDA !FrameIndex,x ;\
CMP !LastFrameIndex,x ;|if last frame is different to new frame then
BNE + ;|do dynamic routine
RTS ;/
+
LDA #$00
XBA
LDA !FrameIndex,x
REP #$30
ASL
TAY
PHY
SEP #$20
LDA ResourceSize,y
STA !Scratch0
REP #$20
TYA
ASL
TAY
PHY
LDA ResourceOffset,y
STA !Scratch1
SEP #$30
LDA !FrameIndex,x
STA !LastFrameIndex,x
TAY
%GetVramDispDynamicRoutine(DZ_DS_Loc_US_Normal)
STA !ScratchD
LDA.l DZ_DS_Loc_US_Normal,x
TAX
LDA #$01
STA.l DZ_DS_Loc_IsValid,x
%DynamicRoutine(!ScratchD, #!GFX00, #!GFX00>>16, !Scratch1, !Scratch0)
REP #$30
PLY
LDA ResourceOffset+2,y
STA !Scratch1
PLY
SEP #$20
LDA ResourceSize+1,y
STA !Scratch0
SEP #$10
BEQ +
LDA !ScratchD
CLC
ADC #$10
STA !ScratchD
%DynamicRoutine(!ScratchD, #!GFX00, #!GFX00>>16, !Scratch1, !Scratch0)
+
        LDX !SpriteIndex
RTS
</pre>
Cluster:
<pre>
DynamicRoutine:
%CheckEvenOrOdd("DZ_DS_Loc_US_Cluster")
BEQ + ;/
RTS
+
%FindSpace("DZ_DS_Loc_US_Cluster,x")
BCS +
        PHX
LDA.l DZ_DS_Loc_US_Cluster,x
TAX
LDA.l DZ_DS_Loc_IsValid,x
BNE ++
        STZ !ClusterSpriteNumber,x
++
        PLX
RTS
+
LDA !ScratchB
BNE +
LDA !FrameIndex,x ;\
CMP !LastFrameIndex,x ;|if last frame is different to new frame then
BNE + ;|do dynamic routine
RTS ;/
+
        PHX
LDA #$00
XBA
LDA !FrameIndex,x
REP #$30
ASL
TAY
PHY
SEP #$20
LDA ResourceSize,y
STA !Scratch0
REP #$20
TYA
ASL
TAY
PHY
LDA ResourceOffset,y
STA !Scratch1
SEP #$30
LDA !FrameIndex,x
STA !LastFrameIndex,x
TAY
%GetVramDispDynamicRoutine(DZ_DS_Loc_US_Cluster)
STA !ScratchD
LDA.l DZ_DS_Loc_US_Cluster,x
TAX
LDA #$01
STA.l DZ_DS_Loc_IsValid,x
%DynamicRoutine(!ScratchD, #!GFX00, #!GFX00>>16, !Scratch1, !Scratch0)
REP #$30
PLY
LDA ResourceOffset+2,y
STA !Scratch1
PLY
SEP #$20
LDA ResourceSize+1,y
STA !Scratch0
SEP #$10
BEQ +
LDA !ScratchD
CLC
ADC #$10
STA !ScratchD
%DynamicRoutine(!ScratchD, #!GFX00, #!GFX00>>16, !Scratch1, !Scratch0)
+
        PLX
RTS
</pre>
</li>
<li></li>
<li></li>
<li></li>
<li></li>
</ol>
=== Creación de Sprites Dinámicos Con Memoria Compartida ===
=== Creación de Sprites Dinámicos Con Memoria Compartida ===
=== Creación de Sprites Dinámicos Con Memoria Mixta ===
=== Creación de Sprites Dinámicos Con Memoria Mixta ===
Line 176: Line 1,693:
== Modo 50% más ==
== Modo 50% más ==
== Widescreen ==
== Widescreen ==
== Mirror de 420B ==
== Optimizaciones de la rutina NMI-Handler ==
== Optimizaciones de la rutina NMI-Handler ==
== Dynamic Z y Dyzen X ==
== Dynamic Z y Dyzen X ==
== Repositorio ==


Enlace de acceso al repositorio: https://github.com/weierstrass1/Dynamic-Z
Dynamic Z es un proyecto es de libre uso, solo se pide agregar a anonimzwx en los créditos de los proyectos donde se use. En caso de querer colaborar con el parche pedir permiso a anonimzwx.
== Donaciones ==
Si deseas apoyar este proyecto puedes hacerlo a traves de Patreon: https://www.patreon.com/user?u=27937024
== Créditos ==
== Créditos ==
El autor original del parche es '''anonimzwx'''.
Agradecimientos especiales a:
* LX5
* LMPuny
* Majorflare
* Usuarios de Fortaleza Reznor
* Usuarios del discord de SNES-Office. https://discord.gg/ARPSauH

Latest revision as of 17:29, 17 October 2020

English Português Español 日本語

Es una librería para el Super Mario World creada por anonimzwx que comunica la PPU (Unidad Procesadora de Imagen) con la lógica de los distintos recursos del ROM mediante un sistema de Mirrors. Actualmente esta en la proceso la versión v3.75.

Funcionalidades

  • Soporte para Sprites Dinámicos: Sprites que pueden cargar el cuadro de animación que necesitan en la memoria de video (VRAM) en cada iteración del juego.
  • Soporte para Sprites Dinámicos con Memoria Compartida: Sprites Dinámicos pero que todas sus copias usan el mismo cuadro de animación al mismo tiempo y usan el mismo espacio en la memoria de vídeo.
  • Soporte para Sprites Semi-Dinámicos: Sprite que carga todos sus cuadros de animación en la memoria de video (VRAM) cuando esta en la pantalla, todas sus copias usan el mismo espacio en la memoria de vídeo.
  • Soporte para Sprites Dinámicos de 2 fases: Sprites Dinámicos pero que cargan la mitad de un cuadro de animación en una iteración y la otra mitad en otra iteración, esto permite un Sprite de gran tamaño pudiendo alcanzar tamaños de hasta 112x112.
  • Soporte para Sprites con Rotación y Re-escalamiento: Sprites Dinámicos pero que pueden rotar sus cuadros de animación o re-escalarlos.
  • Cambiar gráficos en tiempo real.
  • Cambiar paletas de colores en tiempo real.
  • Cambiar tilemaps en tiempo real.
  • Sistema de Tinte-Saturación-Valor (HSV) para paletas de colores.
  • Sistema de Rojo-Verde-Azul (RGB) para paletas de colores.
  • Cambiar bloques del Foreground para modificar el terreno en tiempo real.
  • Cambiar gráficos o paletas de colores del Player en tiempo real, permitiendo Players personalizados.
  • Sistema de OAM propio con prioridad y que permite usar los 128 tiles de la OAM simultáneos.
  • Modo 50% más: Permite eliminar ciertas rutinas de DMA originales del juego para poder procesar mayor cantidad de recursos dinámicos.
  • Widescreen (solo para SA-1): Permite eliminar scanlines del juego para poder procesar mayor cantidad de recursos dinámicos, esto hara que la pantalla se vea como la pantalla panorámica de las películas.
  • Optimizaciones para la rutina de NMI handler.

Complementos

  • DRAdder: Herramienta para añadir recursos dinámicos al ROM como GFX, tilemaps o paletas y comunicarlos con la lógica.
  • Installer: Herramienta para instalar más facil el parche en el rom y seleccionar las funcionalidades deseadas.

Comparativa entre Dynamic Z y DSX

DSX tambien conocido como Dynamic Sprite Patch, es un parche para sprites dinamicos pero que trae varias desventajas.

  • Solo permite un área en la VRAM de 128x32, esto significa que como máximo permite 4 sprites de 32x32 o 1 de 64x64.
  • Utiliza un buffer para armar los cuadros de animación del sprite dinámico, lo que termina siendo lento, ya que, debe transferirlos primero a el buffer y luego transferir el Buffer a la VRAM, esto produce bastante Slowdown y es muy notorio sobretodo en LoRom.
  • El hijack utilizado es riesgoso, bajo ciertas cirscunstancias puede terminar generando glitches gráficos graves.

Dynamic Z V3.5 se supone que dejaría obsoleto al DSX, sin embargo, el staff de SMWC nunca quizo realizar una remoderación de sprites dinámicos a pesar que Dynamic Z V3.5 tenia mayores capacidades en todo aspecto.

Dynamic Z V3.5 permite un area de 128x64, por lo tanto, permite el doble de sprites dinamicos, además permite tambien sprites de 48x48, dando mayor flexibilidad de uso. Si se utiliza el modo 50% más, permite 128x96, 3 veces más que el DSX, además permite sprites de 80x80. Tambien permite sprites de 112x112 y de 96x96 si se utilizan 30FPS.

Dynamic Z V3.75 permite lo mismo que el Dynamic Z V3.5 solo que ahora hay flexibilidad total de los tamaños, además permite incrementar la cantidad de sprites sacrificando Scanlines.

Dynamic Z no tiene glitches graficos y no utiliza buffer además por lo que es mucho más rapido y produce menos slowdown.

Instalación

Utiliza la aplicación "Dynamic Z Installer" que viene incluida con el parche, con ella puedes seleccionar las funcionalidades que trae e instalar el parche facilemente.

DRAdder

DRAdder o Dynamic Resource Adder, es un tool para insertar recursos dinámicos al ROM, esto incluye paletas, gráficos y tilemaps.

Uso

  1. En la carpeta "Dynamic Resources" agrega todos los recursos dinamicos como graficos, paletas o tilemaps que seran insertados al ROM.
  2. En el archivo "ResourceList.txt" veras lo siguiente:
  3. PIXI:
    .Normal
    .Cluster
    .Extended
    OTHER:
    
  4. Para los sprites, agrega en la etiqueta correspondiente los nombres.
  5. PIXI:
    .Normal
    Klaptrap
    Klump
    Zinger
    .Cluster
    Butterfly
    SmallFish
    .Extended
    OTHER:
    
  6. Para otros codigos, como codigos de bloques o codigos de uberasm, puedes añadir la ruta del archivo debajo de la etiqueta OTHER:
  7. PIXI:
    .Normal
    Klaptrap
    Klump
    Zinger
    .Cluster
    Butterfly
    SmallFish
    .Extended
    OTHER:
    ./Uberasm Tool/level/Level105.asm
    
  8. Ejecuta el programa "DynamicResourceAdder.exe" siguiendo las instrucciones. Otra opción es usarlo en la linea de comando con el comando
  9. DynamicResourceAdder <nombre del rom.smc> <lista de recursos.txt> <carpeta de pixi> 

    <lista de recursos.txt> y <carpeta de pixi> son optativos.

Crear Codigos para usarlos con el Tool

En la primera linea del codigo agrega una linea que diga:

;@<nombre de grafico>.bin

Ejemplo:

;@Zinger.bin

El archivo puede ser de cualquier formato, no es necesario que sea ".bin".

Para agregar multiples recursos por archivo deben ir separados por ",", Ejemplo:

;@GFX.bin,Tilemap1.stim

Variables

Hay 2 tipos de variables, estan las variables del parche y los Mirrors de PPU, las variables del parche inician en la dirección $7F0B44 con LoROM o en $418000 con SA-1, mientras que los mirrors de PPU inician en $7FB080 con LoROM o en $418B80 con SA-1. Las variables pueden usar distinta dirección de RAM dependiendo de las funcionalidades elegidas por el usuario, por eso, se requiere usar los archivos de Defines que trae el parche o instalar el parche usando el Tool de instalación.

Generales

Nombre Descripción
DZ_Timer Incrementa en 1 cada vez que se ejecuta el código del parche durante el NMI-Handler. Se utiliza como Timer para sprites Dinámicos o para sincronizar rutinas dinámicas evitando flickering.
DZ_MaxDataPerFrameIn16x16Tiles Máxima cantidad de data que se puede enviar a la VRAM por frame en tiles de 16x16. Por default es 0x10. Puede ser incrementado usando el modo 50% más o sacrificando scanlines. No Modificar Manualmente.
DZ_MaxDataPerFrame Similar a DZ_MaxDataPerFrameIn16x16Tiles pero en bytes. No modificar Manualmente.
DZ_CurrentDataSend Cantidad de data enviada en el frame actual. No modificar manualmente, puede ser leida para saber si realizar una rutina dinamica en el frame actual. Para esto se debe revisar si lo que se quiere enviar más lo que esta en esta variable es mayor a DZ_MaxDataPerFrame.
DZ_FreeRams Direcciones de RAM que pueden utilizarse libremente, al momento de insertar el parche se puede saber cuantas hay disponibles.

Sprites Dinámicos

Nombre Tamaño Descripción
DZ_DS_Length 1 Tamaño de la lista de sprites dinámicos, Tamaño máximo de la lista es 0x30. No modificar manualmente.
DZ_DS_LastSlot 1 Ultimo Slot de la lista. No modificar manualmente.
DZ_DS_FirstSlot 1 Primer Slot de la lista. No modificar manualmente.
DZ_DS_MaxSpace 1 Espacio máximo asignado en la VRAM para sprites dinámicos en tiles de 16x16. Por default es 0x20.
DZ_DS_FindSpaceMethod 1 Si es 1 los sprites dinámicos se acomodan partiendo desde el fondo del espacio asignado en la VRAM. Si es 0 se acomodan partiendo desde el Tope del espacio asignado en la VRAM. Por Default es 1.
DZ_DS_StartingVRAMOffset 2 Dirección en la VRAM desde donde inicia el espacio asignado para sprites dinámicos.
DZ_DS_StartingVRAMOffset8x8Tiles 1 Similar a DZ_DS_StartingVRAMOffset pero en en bloques de 8x8.
DZ_DS_TotalSpaceUsed 1 Espacio total usado por todos los sprites dinámicos en la VRAM. No modificar manualmente.
DZ_DS_TotalSpaceUsedOdd 1 Espacio Total usado por todos los sprites dinámicos en frames impares. No modificar manualmente.
TotalSpaceUsedEven 1 Espacio Total usado por todos los sprites dinámicos en frames pares. No modificar manualmente.
DZ_DS_TotalDataSentOdd 1 Cantidad de data enviada a la VRAM enviada por sprites en frames impares. No modificar manualmente.
TotalDataSentEven 1 Cantidad de data enviada a la VRAM enviada por sprites en frames impares. No modificar manualmente.

Las siguientes son tablas de 0x30 bytes que representan la información de cada sprite dinámico.

Nombre Descripción
DZ_DS_Loc_UsedBy Se utiliza para vincular a un sprite con un Slot Dinámico. Usa el formato S TT IIIII, S = Si es Shared Dynamic o no. TT => 00 es sprite Normal, 01 Es Cluster Sprite, 10 Es Extended Sprite y 11 es Overworld Sprite. IIIII es el Indice del Sprite.
DZ_DS_Loc_SpriteNumber Es el ID Del Sprite. Se Utiliza para saber si el sprite vinculado a el Slot sigue siendo valido.
DZ_DS_Loc_SpaceUsedOffset Indica en que lugar de la VRAM inicia el espacio asignado al slot de sprite dinámico. Esta en bloques de 16x16.
DZ_DS_Loc_SpaceUsed La cantidad de espacio que requiere el Sprite en la VRAM. Esta en bloques de 16x16.
DZ_DS_Loc_IsValid Si es 0, el sprite dinámico aun no ha realizado su rutina dinámica y no por lo tanto, no puede realizar su rutina gráfica.
DZ_DS_Loc_FrameRateMethod Si es 0 puede enviar data a la VRAM en cualquier frame, si es 1 solo en frame impares y si es 2 solo en frame pares.
DZ_DS_Loc_NextSlot Link hacia el siguiente slot de la lista. No modificar manualmente.
DZ_DS_Loc_PreviewSlot Link hacia el slot anterior de la lista. No modificar manualmente.

Las siguientes tablas indican que slot de la lista usa cada sprite dinámico.

Nombre Tamaño
DZ_DS_Loc_US_Normal Lorom: 12 - SA-1: 22
DZ_DS_Loc_US_Cluster 20
DZ_DS_Loc_US_Extended 10
DZ_DS_Loc_US_OW 16

Sprites Dinámicos con Memoria Compartida

Las siguientes son tablas de 0x30 bytes asignadas a un slot de sprite dinámico.

Nombre Descripción
DZ_DS_Loc_SharedUpdated Indica si el el slot de sprite dinámico ya realizo su rutina dinámica durante este frame, después de procesar todas las copias del sprite dinámico con memoria compartida, se pondrá en 0.
DZ_DS_Loc_SharedFrame Indica que cuadro de animación deben mostrar todas las copias del sprite dinámico con memoria compartida.

Sprites Semi-Dinámicos

Las variables de Sprites Semi-Dinámicos estan conformadas por una tablas por cada tipo de sprite.

Offset indica la posición en la VRAM en la que se alojaran los gráficos del sprite.

0x00 = X 0x01 = X+$100 0x02 = X+$200 0x03 = X+$300 0x04 = X+$400 0x05 = X+$500 0x06 = X+$600 0x07 = X+$700 0x08 = X+$800 0x09 = X+$900 0x0A = X+$A00 0x0B = X+$B00 0x0C = X+$C00 0x0D = X+$D00 0x0E = X+$E00 0x0F = X+$F00

X puede ser $6000 si se elije SP1/SP2 o $7000 si se elije SP3/SP4.

Cuidado: El offset debe ser menor a $10-Cantidad de lineas de 8 pixeles de alto usadas por el sprite, por ejemplo si el espacio en la VRAM utilizado por el sprite es 4 lineas, debe elegirse C o menos.

Nombre Tamaño
DZ_SDS_Offset_Normal Lorom: 12 - SA-1: 22
DZ_SDS_Offset_Cluster 20
DZ_SDS_Offset_Extended 10
DZ_SDS_Offset_OW 16

PaletteAndPage tiene el siguiente formato:

0000PPPT:

PPP Es la paleta utilizada por el sprite:

000 = Paleta 8 001 = Paleta 9 010 = Paleta A 011 = Paleta B 100 = Paleta C 101 = Paleta D 110 = Paleta E 111 = Paleta F

T es la pagina de graficos usada, 0 es SP1/SP2, 1 es SP3/SP4.

Nombre Tamaño
DZ_SDS_PaletteAndPage_Normal Lorom: 12 - SA-1: 22
DZ_SDS_PaletteAndPage_Cluster 20
DZ_SDS_PaletteAndPage_Extended 10
DZ_SDS_PaletteAndPage_OW 16

Size es la cantidad de bloques de 8x8 usados por el sprite en la VRAM.

Nombre Tamaño
DZ_SDS_Size_Normal Lorom: 12 - SA-1: 22
DZ_SDS_Size_Cluster 20
DZ_SDS_Size_Extended 10
DZ_SDS_Size_OW 16

Valid indica si el sprite ya envio todos sus gráficos en la VRAM, mientras es 0, el sprite no debe ejecutarse.

Nombre Tamaño
DZ_SDS_Valid_Normal Lorom: 12 - SA-1: 22
DZ_SDS_Valid_Cluster 20
DZ_SDS_Valid_Extended 10
DZ_SDS_Valid_OW 16

SendOffset utilizado para realizar la rutina dinámica, este indica desde que posición debe empezar la rutina dinámica. Al inicio es igual a offset, pero si no logra enviar toda la información en un solo frame, entonces aumenta según en que posición quedo, para asi seguir enviando data en el siguiente frame.

Nombre Tamaño
DZ_SDS_SendOffset_Normal Lorom: 12 - SA-1: 22
DZ_SDS_SendOffset_Cluster 20
DZ_SDS_SendOffset_Extended 10
DZ_SDS_SendOffset_OW 16

SpriteNumber es el SpriteNumber del sprite, se registra para poder saber si sigue siendo valido el slot o no.

Nombre Tamaño
DZ_SDS_SpriteNumber_Normal Lorom: 12 - SA-1: 22
DZ_SDS_SpriteNumber_Cluster 20
DZ_SDS_SpriteNumber_Extended 10
DZ_SDS_SpriteNumber_OW 16

PaletteLoaded si la paleta de colores no ha sido cargada entonces es 0, sino es 1.

Nombre Tamaño
DZ_SDS_PaletteLoaded_Normal Lorom: 12 - SA-1: 22
DZ_SDS_PaletteLoaded_Cluster 20
DZ_SDS_PaletteLoaded_Extended 10
DZ_SDS_PaletteLoaded_OW 16

Sprites Dinámicos de 2 fases

Sprites con Rotación y Re-escalamiento

Cambio de Gráficos y Tilemaps

Se utiliza una lista de transferencias hacia la VRAM. Luego de realizar las transferencias, DZ_PPUMirrors_VRAM_Transfer_Length sera 0xFF. Si DZ_PPUMirrors_VRAM_Transfer_Length es 0xFF la lista esta vacia y no se realiza ninguna transferencia. Para utilizarlo, se recomienda usar las macros que vienen incluidos con el parche.

Nombre Tamaño Descripción
DZ_PPUMirrors_VRAM_Transfer_Length 1 Tamaño de la lista de transferencias de DMA a la VRAM. Tamaño máximo 128.
DZ_PPUMirrors_VRAM_Transfer_SourceLength 256 Tabla que tiene el tamaño en bytes del recurso que se enviara. 2 bytes por transferencia.
DZ_PPUMirrors_VRAM_Transfer_Offset 256 Tabla que tiene la Dirección de la VRAM donde llegara el recurso que se enviara. 2 bytes por transferencia.
DZ_PPUMirrors_VRAM_Transfer_Source 256 Tabla que tiene la Dirección del recurso que se enviara. 2 bytes por transferencia.
DZ_PPUMirrors_VRAM_Transfer_PPUSourceBNK 256 Tabla que tiene el BNK del recurso que se enviara. 2 bytes por transferencia (el high byte no se toma en cuenta).

Cambio de Bloques

Cambio de Gráficos del Player

Nombre Tamaño Descripción
DZ_Player_GFX_Enable 1 Si es 1, realiza la rutina dinámica normal del player, si es 0, se deshabilita permitiendo hacer una rutina dinámica personalizando.
DZ_Player_GFX_Addr 2 Indica la dirección donde inician los graficos del player, puede modificarse para cambiar los gráficos por otro player que use el mismo esquema que los gráficos originales. Por default es 0x2000.
DZ_Player_GFX_BNK 2 Indica el BNK donde estan almacenados los graficos del player, puede modificarse para cambiar los gráficos por otro player que use el mismo esquema que los gráficos originales. El High byte debe ser 0x00, por default es 0x007F.

Cambio de Paletas de colores

Sistema de Tinte-Saturación-Valor (HSV)

Sistema de Rojo-Verde-Azul (RGB)

Cambio de Paletas de colores del Player

Nombre Tamaño Descripción
DZ_Player_Palette_Enable 1 Si es 1, se realiza la rutina default de cambio de paleta del player. Si es 0, no se realiza esa rutina, permitiendo una personalizada.
DZ_Player_Palette_Addr 2 Indica la dirección de memoria donde se almacena la paleta del player, puede modificarse para cambiar los colores en la paleta de colores 8 iniciando del color 0x86. Por default tiene el valor dado por la dirección de WRAM $7E0D82.
DZ_Player_Palette_BNK 1 Indica el BNK donde se almacena la paleta del player, puede modificarse para cambiar los colores en la paleta de colores 8 iniciando del color #$86. Por default es 0x00.
DZ_PPUMirrors_CGRAM_LastPlayerPal 2 Almacena la dirección de memoria de la ultima paleta que fue cargada. Si su valor es 0xFFFF y se utiliza la rutina de cambio de paleta default, se forzara el rutina de cambio de paleta en ese frame.

Sistema de OAM

Modo 50% más

Widescreen

Se utiliza una rutina de HDMA para la realizar el widescreen, luego de eso, se calcula la máxima cantidad de data que se puede enviar a la VRAM.

Nombre Tamaño Descripción
DZ_PPUMirrors_WS_Enable 1 Si es 0, no se realiza la rutina de HDMA, si es 1 se realiza.
DZ_PPUMirrors_WS_Buffer 435 Un buffer que tiene 2 bytes por scanline, el primer byte de cada scanline tiene el alto de la scanline y el segundo es el valor del registro 2100, al final del buffer se pone el valor 0xFF. Este buffer debe ser escrito usando las macros que vienen con el parche.

Otros

Macros

Generales

Mul(a, b)

Multiplica dos números de 8 bits.

Parametros
  • a: multiplicando 1.
  • b: multiplicando 2.
Retorno

El resultado queda almacenado en !MultiplicationResult, Se debe esperar 8 cyclos antes de leer el resultado (Solo 5 si se usa SA-1).

MulAfterMul(b)

Multiplica un número de 8 bits con el resultado de una multiplicación anterior.

Parametros
  • b: Número a multiplicar
Retorno

El resultado queda almacenado en !MultiplicationResult, Se debe esperar 8 cyclos antes de leer el resultado (Solo 5 si se usa SA-1).

MulAfterDiv(b)

Multiplica un número de 8 bits con el resultado de una división anterior.

Parametros
  • b: Número a multiplicar
Retorno

El resultado queda almacenado en !MultiplicationResult, Se debe esperar 8 cyclos antes de leer el resultado (Solo 5 si se usa SA-1).

MulW(a, b)

Similar a Mul pero no requiere esperar antes de leer el resultado.

MulWAfterMul(b)

Similar a MulAfterMul pero no requiere esperar antes de leer el resultado.

MulWAfterDiv(b)

Similar a MulAfterDiv pero no requiere esperar antes de leer el resultado.

Div(ah, al, b)

Divide un número de 16 bits por un número de 8 bits.

Parametros
  • ah: High byte del dividendo.
  • al: High byte del dividendo.
  • b: Divisor.
Retorno

El resultado queda almacenado en !DivisionResult, se deben esperar 16 ciclos (5 si se usa SA-1).

DivAfterMul(b)

Divide el resultado de una multiplicación anterior por un número de 8 bits.

Parametros
  • b: Divisor.
Retorno

El resultado queda almacenado en !DivisionResult, se deben esperar 16 ciclos (5 si se usa SA-1).

DivAfterDiv(b)

Divide el resultado de una división anterior por un número de 8 bits.

Parametros
  • b: Divisor.
Retorno

El resultado queda almacenado en !DivisionResult, se deben esperar 16 ciclos (5 si se usa SA-1).

DivW(ah, al, b)

Similar a Div pero no requiere esperar antes de leer el resultado.

DivWAfterMul(b)

Similar a DivAfterMul pero no requiere esperar antes de leer el resultado.

DivWAfterDiv(b)

Similar a DivAfterDiv pero no requiere esperar antes de leer el resultado.

Sprites Dinámicos

FindSpace(DSSlotUsed)

Se utiliza para que el Sprite Dinámico busque un espacio libre más adecuado para poner los gráficos y asi se defragmente la VRAM.

Parametros

Recibe una de las direcciones de tipo "DZ_DS_Loc_US_XXXX", debe usarse la que corresponde al tipo de sprite, por ejemplo, si se utiliza en un sprite normal debe usarse "DZ_DS_Loc_US_Normal,x"

Retorno

Si retorna Carry Clear el sprite no encontro ninguna posición valida sus gráficos, si el sprite no es Valido, debe ser vuelto a cargar, Si retorna Carry Set se puede realizar la rutina dinamica.

Si la dirección $0B luego de usar esta macro no es 0x00, entonces debe forzarse la rutina dinámica en este frame.

CheckSlot(FrameRateMode, NumberOf16x16Tiles, SpriteNumber, SpriteTypeAndSlot, SpriteUsedSlot)

Remueve de la lista los Sprites Dinámicos que ya no esten vivos o sean invalidos, además busca un espacio en la lista de sprites dinámicos para un sprite nuevo. Solo debe ser usada para Cluster, Extended u OW sprites, para sprites normales usar CheckSlotNormalSprite.

Parametros
  • FrameRateMode: Indica en que frames puede enviar Gráficos a la VRAM.
    • 00: Se le asigna automaticamente de forma balanceada.
  • NumberOf16x16Tiles: Cantidad de tiles de 16x16 utilizados por el Sprite Dinámico.
  • SpriteNumber: Dirección que almacena el Sprite Number del sprite dinámico, por ejemplo, en el caso de los clusters seria "$1892|!Addr,x".
  • SpriteTypeAndSlot: Indica de que tipo de sprite es y si es un sprite usa o no memoria compartida. tiene el siguiente formato:
    • STT0 0000
    • S: Si usa memoria compartida debe ser 1, sino es 0.
    • TT: El tipo de sprite, 00 es Normal, 01 es Cluster, 10 es Extended y 11 es OW.

SpriteUsedSlot: Debe usarse una de las direcciones DZ_DS_Loc_US_XXXX segun corresponda, por ejemplo, para Cluster seria DZ_DS_Loc_US_Cluster.

Retorno

Si no hay espacio disponible en la VRAM, la lista de sprites dinámicos esta llena o si se excede la cantidad máxima de data que se puede enviar en el frame, el sprite sera eliminado.

CheckSlotNormalSprite(NumberOf16x16Tiles, SpriteTypeAndSlot)

Similar a CheckSlot pero para Sprites Dinámicos.

Parametros
  • NumberOf16x16Tiles: Cantidad de tiles de 16x16 utilizados el sprite dinámico.
  • SpriteTypeAndSlot: Si el sprite usa memoria compartida, debe ser 0x80, sino debe ser 0x00.
Retorno

Si no hay espacio disponible en la VRAM, la lista de sprites dinámicos esta llena o si se excede la cantidad máxima de data que se puede enviar en el frame, el sprite sera eliminado y se podre ser respawneado.

DynamicRoutine(VRAMOffset, ResourceAddr, ResourceBNK, ResourceOffset, size)

Establece los mirros de VRAM necesarios para que el sprite envie la información a la PPU. Solo puede enviar memoria continua a la VRAM, por lo que si el frame usa memoria discontinua en la VRAM o en el recurso, debe llamarse más de una vez con distintos parametros.

Parametros
  • VRAMOffset: Indica a que posición de la VRAM llegaran los graficos, debe estar en Tiles de 8x8. La data llegara a la Dirección de VRAM DZ_DS_StartingVRAMOffset + (0x10*VRAMOffset). Se puede conseguir usando la Macro GetVramDispDynamicRoutine(DSLocUS).
  • ResourceAddr: La dirección de memoria donde estan almacenados los gráficos del sprite.
  • ResourceBNK: El BNK donde estan almacenados los gráficos del sprite.
  • ResourceOffset: La posición donde inicia el frame que debe enviarse a la VRAM dentro del GFX.
  • size: Cantidad de data en tiles de 8x8 que deben enviarse a la VRAM.

GFXTabDef(index)

Utilizado por Dynamic Resource Adder para identificar donde están almacenadas las direcciones de los recursos usados por el el código, al usarlo genera el Define !GraphicsTable que tiene la dirección de memoria donde esta guardada la tabla que especifica donde estan guardados los recursos dinámicos usados por el código.

GFXDef(offset)

Utilizado por Dynamic Resource Adder para identificar donde esta almacenado uno de los recursos usados por el código, al usarlo genera el Define !GFXZZ donde ZZ es es un numero en hexadecimal que es el ID del recurso (Ejemplo: 00, 01, etc.) y que tiene la dirección de memoria donde esta guardado el recurso dinámico.

CheckEvenOrOdd(DSLocUS)

Utilizado para saber si un sprite dinámico de 30FPS debe cambiar de frame o debe realizar su rutina dinámica.

Parametros
  • DSLocUS: Debe ser una de las direcciones "DZ_DS_Loc_US_XXXX" por ejemplo: "DZ_DS_Loc_US_Normal".
Retorno

Si despues de usar esta rutina el Flag N es Clear entonces puede realizar su rutina dinámica, sino puede hacer cambio de Frame.

GetVramDisp(DSLocUS)

Utilizado para obtener el Offset desde donde se deben empezar a realizar la rutina Gráfica del sprite.

Parametros
  • DSLocUS: Debe ser una de las direcciones "DZ_DS_Loc_US_XXXX" por ejemplo: "DZ_DS_Loc_US_Normal".
Retorno

A = Offset para los tiles de la rutina gráfica.

GetVramDispDynamicRoutine(DSLocUS)

Utilizado para obtener el Offset desde donde se deben llegar los gráficos en la VRAM en la rutina Dinámica del sprite.

Parametros
  • DSLocUS: Debe ser una de las direcciones "DZ_DS_Loc_US_XXXX" por ejemplo: "DZ_DS_Loc_US_Normal".
Retorno

A = Offset para la data de la rutina dinámica.

RemapOamTile(Tile, Offset)

Utilizado para remappear los tiles en la rutina Gráfica.

Parametros
  • Tile: Tile que debe remappearse.
  • Offset: Resultado obtenido de %GetVramDisp(DZ_DS_Loc_US_Normal).

Sprites Dinámicos con Memoria Compartida

CheckSlotNormalSpriteNoRemove(NumberOf16x16Tiles, SpriteTypeAndSlot)

Similar a CheckSlotNormalSprite pero no elimina el sprite si falla.

Parametros
  • NumberOf16x16Tiles: Cantidad de tiles de 16x16 utilizados el sprite dinámico.
  • SpriteTypeAndSlot: Si el sprite usa memoria compartida, debe ser 0x80, sino debe ser 0x00.
Retorno

Si no hay espacio disponible en la VRAM, la lista de sprites dinámicos esta llena o si se excede la cantidad máxima de data que se puede enviar en el frame, retorna Carry Clear, sino Carry Set.

Sprites Semi-Dinámicos

Sprites Dinámicos de 2 fases

Sprites con Rotación y Re-escalamiento

Cambio de Gráficos y Tilemaps

ForcedTransferToVRAM(VRAMOffset, ResourceAddr, ResourceBNK, Lenght)

Setea la información necesaria en los Mirrors de VRAM, para enviar gráficos a la PPU.

Parametros
  • VRAMOffset: Dirección de VRAM a la que llegara la data.
  • ResourceAddr: Dirección del recurso que se enviara a la VRAM.
  • ResourceBNK: BNK del recurso que se enviara a la VRAM. Debe ser de 2 Bytes, el highbyte debe ser 0x00.
  • Lenght: Cantidad de información que se enviara a la VRAM.

TransferToVRAM(VRAMOffset, ResourceAddr, ResourceBNK, Lenght)

Similar a ForcedTransferToVRAM pero no produce Flickering. Puede fallar si se excede la cantidad máxima de información a enviar durante el NMI Handler.

Parametros
  • VRAMOffset: Dirección de VRAM a la que llegara la data.
  • ResourceAddr: Dirección del recurso que se enviara a la VRAM.
  • ResourceBNK: BNK del recurso que se enviara a la VRAM. Debe ser de 2 Bytes, el highbyte debe ser 0x00.
  • Lenght: Cantidad de información que se enviara a la VRAM.
Retorno

Si el Carry es Set, entonces se pudo realizar la rutina, sino Fallo.

Cambio de Bloques

Cambio de Gráficos del Player

Cambio de Paletas de colores

ForcedTransferToCGRAM(CGRAMOffset, ResourceAddr, ResourceBNK, Lenght)

Setea la información necesaria en los Mirrors de CGRAM, para enviar gráficos a la PPU.

Parametros
  • CGRAMOffset: Dirección de CGRAM a la que llegara la data.
  • TableAddr: Dirección del recurso que se enviara a la CGRAM.
  • TableBNK: BNK del recurso que se enviara a la CGRAM.
  • Lenght: Cantidad de información que se enviara a la CGRAM.

TransferToCGRAM(CGRAMOffset, ResourceAddr, ResourceBNK, Lenght)

Similar a ForcedTransferToCGRAM pero no produce Flickering. Puede fallar si se excede la cantidad máxima de información a enviar durante el NMI Handler.

Parametros
  • CGRAMOffset: Dirección de CGRAM a la que llegara la data.
  • TableAddr: Dirección del recurso que se enviara a la CGRAM.
  • TableBNK: BNK del recurso que se enviara a la CGRAM.
  • Lenght: Cantidad de información que se enviara a la CGRAM.
Retorno

Si el Carry es Set, entonces se pudo realizar la rutina, sino Fallo.

Sistema de Tinte-Saturación-Valor (HSV)

Sistema de Rojo-Verde-Azul (RGB)

Cambio de Paletas de colores del Player

Sistema de OAM

Modo 50% más

Widescreen

Otros

Rutinas

Generales

Sprites Dinámicos

Sprites Dinámicos con Memoria Compartida

Sprites Semi-Dinámicos

Sprites Dinámicos de 2 fases

Sprites con Rotación y Re-escalamiento

Cambio de Gráficos y Tilemaps

Cambio de Bloques

Cambio de Gráficos del Player

Cambio de Paletas de colores

Sistema de Tinte-Saturación-Valor (HSV)

Sistema de Rojo-Verde-Azul (RGB)

Cambio de Paletas de colores del Player

Sistema de OAM

Modo 50% más

Widescreen

Otros

Sprites Dinámicos

Un Sprite Dinámico es un Sprite que sus gráficos se cargan en la VRAM a medida que son requeridos. Existen distintos tipos de Sprite dinámico:

  • Sprites Dinámicos Con Memoria Compartida: Es un Sprite Dinámico en que todas las copias de él, comparten el mismo cuadro de animación en todo momento.
  • Sprites Dinámicos Con Memoria Mixta: Es un Sprite Dinámico que puede usar memoria compartida solo para ciertas animaciones.
  • Sprites Semi-Dinámicos: Es un Sprite que carga en la VRAM todos los gráficos que necesita cuando es utilizado, luego todas las copias de dicho sprite, utilizan el mismo espacio en la VRAM para los gráficos.
  • Sprites Dinámicos de 2 fases: Es un Sprite Dinámico pero cada frame requiere 2 loops para ser cargado en la VRAM, por lo tanto, la velocidad de animación máxima es 30FPS. Estos sprites suelen ser de gran tamaño y están pensados para la realización de jefes.
  • Sprites con Rotación y Re-escalamiento: Es un Sprite Dinámico pero que al empezar el nivel, calcula sus frames con distintos ángulos de rotación o distintos tamaños y luego los utiliza para sus animaciones. Se recomienda siempre dentro de lo posible, no utilizarlos, ya que, el algoritmo de rotación y escalamiento utiliza vecinos más cercanos y los cuadros de animación terminan con mala calidad, además, requieren un Setup usando uberasm, se recomienda redibujar cada frame a mano o hacer sus gráficos con un Tool Externo, solo se recomienda utilizar en caso que se tenga poco espacio en el ROM.

Uso

  1. Usando el Dynamic Resource Adder (DRAdder), agrega todos los recursos a la carpeta Dynamic Resources.
  2. Agrega el nombre del sprite en la lista "ResourceList.txt" en la zona que corresponda.
  3. Inserta los recursos con el DRAdder e inserta el sprite con pixi.

Uso de Sprites Dinámicos (Con o sin Memoria compartida)

El primer extra byte tiene el siguiente formato:

TT?? ????

TT = Asigna si el sprite usa solo frames impares o pares para enviar sus graficos a la VRAM. 00 = Asigna de forma automatica asignando de forma balanceada, 01 = Solo envia en frames pares, 10 = Envia en solo frames impares, 11 = No usado.

Uso de Sprites Semi-Dinámicos

El primer extra byte tiene el siguiente formato:

GGGG PPP T

  • GGGG = Asigna en que linea de 128x8 de la VRAM se enviaran los grafico donde 0 es el tope y F es el fondo.
  • PPP = Paleta que usara el sprite
    • 000 = Paleta 8
    • 001 = Paleta 9
    • 010 = Paleta A
    • 011 = Paleta B
    • 100 = Paleta C
    • 101 = Paleta D
    • 110 = Paleta E
    • 111 = Paleta F
  • T = Si es 0 Usa SP1 y SP2, Si es 1 Usa SP3 y SP4.

Creación de Sprites Dinámicos

Para crear un sprite dinámico sigue los siguientes pasos:

  1. Define una ram que usaras para almacenar el ultimo cuadro de animación que utilizo el sprite y otra para el cuadro de animación actual. Ejemplo:
    !LastFrameIndex = !SpriteMiscTable6
    !FrameIndex = !SpriteMiscTable1
    
  2. En el Sprite Init has que inicie !LastFrameIndex en 0xFF, !FrameIndex ponle el valor del primer frame que se mostrara (yo le pondre 0).
    	LDA #$FF
    	STA !LastFrameIndex,x
            STZ !FrameIndex,x
    
  3. Llama en el sprite init, la macro CheckSlotNormalSprite si es un sprite normal o CheckSlot si es Cluster, Extended u OW Sprite. Normal:
    	%CheckSlotNormalSprite(#$0C, $00)
    

    El primer parametro es la cantidad de tiles de 16x16 que usa el sprite como máximo y el segundo parametro debe ser $00. Si el sprite es de 60FPS, setea el Extra Byte 1 en #$E0 antes de llamarla. Otros:

    	%CheckSlot(#$00, #$01, "!ClusterNumber,x", $20, DZ_DS_Loc_US_Cluster)
    

    El primer parametro normalmente es #$00, pero podrias ponerle #$01 si el sprite solo envia graficos en frames pares, #$02 si quieres que solo envie graficos en frames impares o #$03 si es un sprite de 60FPS. El segundo parametro es la cantidad de tiles de 16x16 que usa el sprite. Los otros parametros dependen del tipo:

    Cluster: "!ClusterSpriteNumber,x", $20, DZ_DS_Loc_US_Cluster Extended: "!ExtendedSpriteNumber,x", $40, DZ_DS_Loc_US_Extended OW: "!OWSpriteNumber,x", $60, DZ_DS_Loc_US_OW

  4. Luego al inicio del SpriteCode antes de llamar a la rutina gráfica, añade esto:
    	JSR DynamicRoutine
    
    	LDA DZ_DS_Loc_US_Normal,x
    	TAX
    
    	LDA DZ_DS_Loc_IsValid,x
    	BNE +
    	LDX !SpriteIndex
    RTS
    +
    	LDX !SpriteIndex 
    

    La primera linea llama a la rutina dinamica, las demás chequean si el sprite es valido, si el sprite es invalido el sprite no se ejecuta. El sprite es invalido si no ha podido realizar su rutina dinámica aun. DZ_DS_Loc_US_Normal Depende del tipo de sprite, Normal es para sprites normales, Cluster para clusters y asi respectivamente.

  5. Para generar la rutina Dinámico iremos paso a paso. Lo primero es tener el GFX, para esto puedes utilizar Dyzen que generara todas las tablas por ti, sino debes generar el GFX por ti mismo, para esto lo importante es que cada cuadro de animación use memoria continua, ejemplo:

    Una vez que tenemos todos los frames hechos, debemos hacer las tablas, necesitamos 2 tablas ResourceOffset y ResourceSize.

    • ResourceOffset: Cada valor es de 16 bits (dw), por cada cuadro de animación necesita 2 valores, el primero es la posición en que esta guardado el cuadro de animación, el segundo es donde inicia la ultima linea de 8x8 usada por el sprite. Ejemplo si asi debe quedar guardado el cuadro de animación en la VRAM.

    Y el GFX luce asi:

    El primer valor seria $0000, ya que el cuadro de animación esta al inicio del GFX, mientras que la segunda linea esta a 35 tiles de 8x8 del inicio del GFX, cada tile de 8x8 usa 0x20 bytes, por lo tanto el segundo valor sera $0460

    Si tuviera un segundo frame por ejemplo:

    Y debe verse asi en la VRAM:

    El segundo frame parte en la posición final del primer frame ($0460) más la cantidad de tiles de 8x8 usadas por la segunda linea del primer frame (2 tiles), por lo tanto, el primer valor seria $04A0, la segunda linea seria estaria a 36 tiles desde esa posición por lo que seria 36*0x20 = $0920.

    • ResourceSize: Similar a ResourceOffset, tambien usa 2 valores, pero son de 8 bitx (db), Cada valor indica cuantos tiles de 8x8 necesita cada transferencia, por lo tanto, siguiendo con el ejemplo anterior, el primer cuadro de animación requiere 2 transferencia, uno de 35 tiles (0x23) y otra de 2 tiles, por lo tanto, el primer valor seria $23 y el segundo $02. Para el segundo frame seria uno de 36 tiles (0x24) y otra de 2 tiles, por lo tanto seria $24 y $02. Importante: No siempre se necesita poner la ultima linea, Si el frame completo usa solo tiles de 16x16, el segundo valor debe ser 0.

    Aqui un ejemplo de las tablas de Klump:

    ResourceOffset:
    Walk0_ResourceOffset:
    	dw $0000,$0460
    Walk1_ResourceOffset:
    	dw $04A0,$0920
    Walk2_ResourceOffset:
    	dw $0960,$0E00
    Walk3_ResourceOffset:
    	dw $0E80,$1300
    Walk4_ResourceOffset:
    	dw $1380,$1800
    Walk5_ResourceOffset:
    	dw $1880,$1CE0
    Walk6_ResourceOffset:
    	dw $1CE0,$2140
    Walk7_ResourceOffset:
    	dw $2180,$2620
    Flip0_ResourceOffset:
    	dw $26A0,$2B00
    Resist0_ResourceOffset:
    	dw $2B40,$2FC0
    Death0_ResourceOffset:
    	dw $3000,$34E0
    Death1_ResourceOffset:
    	dw $35A0,$3A40
    Death2_ResourceOffset:
    	dw $3AC0,$3F80
    Death3_ResourceOffset:
    	dw $4040,$4500
    Death4_ResourceOffset:
    	dw $45C0,$49E0
    Death5_ResourceOffset:
    	dw $49E0,$4DE0
    Death6_ResourceOffset:
    	dw $4DE0,$51E0
    
    
    ResourceSize:
    Walk0_ResourceSize:
    	db $23,$02
    Walk1_ResourceSize:
    	db $24,$02
    Walk2_ResourceSize:
    	db $25,$04
    Walk3_ResourceSize:
    	db $24,$04
    Walk4_ResourceSize:
    	db $24,$04
    Walk5_ResourceSize:
    	db $23,$00
    Walk6_ResourceSize:
    	db $23,$02
    Walk7_ResourceSize:
    	db $25,$04
    Flip0_ResourceSize:
    	db $23,$02
    Resist0_ResourceSize:
    	db $24,$02
    Death0_ResourceSize:
    	db $27,$06
    Death1_ResourceSize:
    	db $25,$04
    Death2_ResourceSize:
    	db $26,$06
    Death3_ResourceSize:
    	db $26,$06
    Death4_ResourceSize:
    	db $21,$00
    Death5_ResourceSize:
    	db $20,$00
    Death6_ResourceSize:
    	db $20,$00
    
  6. Crea una rutina llamada DynamicRoutine.
  7. DynamicRoutine:
    RTS
    

    Si usas la macro %EasyDynamicRoutine, puedes saltarte los pasos siguientes, sin embargo igual se explicaran debido a que puede que necesites hacer una rutina dinámica más flexible. Si usas %EasyDynamicRoutine, solamente necesitas lo siguiente: WIP

  8. Primero se hacen los chequeos para saber si el sprite puede o no realizar la rutina gráfica, el primero chequeo es si el el frame actual concuerda con el tipo de frame en que tiene permitido el sprite dinámico realizar su rutina. Para esto usamos:
    DynamicRoutine:
    	%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
    	BEQ +								
    RTS
    +
    RTS
    

    "DZ_DS_Loc_US_Normal" Cambia segun el tipo de sprite. Este chequeo no es necesario si el sprite es de 60FPS.

  9. El segundo chequeo se realiza con la macro %FindSpace, Si retorna Carry Clear y el sprite es invalido debemos eliminar el sprite, si es valido se debe salir de la rutina dinámica, si el Carry es Set puede realizar su rutina dinámica y si $0B es distinto de 0, debe forzarse la rutina dinámica saltandose los chequeos siguientes. Normal:
    DynamicRoutine:
    	%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
    	BEQ +								;/
    RTS
    +
    	%FindSpace("DZ_DS_Loc_US_Normal,x")
    	BCS +
    
    	LDA.l DZ_DS_Loc_US_Normal,x
    	TAX
    
    	LDA.l DZ_DS_Loc_IsValid,x
    	BNE ++
            LDX $15E9|!addr
            STZ !SpriteStatus,x
            LDA !SpriteLoadStatus,x
            TAX
            LDA #$00
            STA !SpriteLoadTable,x
    ++
            LDX $15E9|!addr
    RTS
    +
    	LDA !ScratchB
    	BNE +
    +
    RTS
    

    Cluster:

    DynamicRoutine:
    	%CheckEvenOrOdd("DZ_DS_Loc_US_Cluster")
    	BEQ +								;/
    RTS
    +
    	%FindSpace("DZ_DS_Loc_US_Cluster,x")
    	BCS +
    
            PHX
    	LDA.l DZ_DS_Loc_US_Cluster,x
    	TAX
    
    	LDA.l DZ_DS_Loc_IsValid,x
    	BNE ++
            STZ !ClusterSpriteNumber,x
    ++
            PLX
    RTS
    +
    	LDA !ScratchB
    	BNE +
    +
    RTS
    
  10. El siguiente chequeo es para verificar que no se envie un frame que ya esta cargado en la VRAM. Para esto se usa las tablas que definimos en el paso 1: Normal:
    DynamicRoutine:
    	%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
    	BEQ +								;/
    RTS
    +
    	%FindSpace("DZ_DS_Loc_US_Normal,x")
    	BCS +
    
    	LDA.l DZ_DS_Loc_US_Normal,x
    	TAX
    
    	LDA.l DZ_DS_Loc_IsValid,x
    	BNE ++
            LDX $15E9|!addr
            STZ !SpriteStatus,x
            LDA !SpriteLoadStatus,x
            TAX
            LDA #$00
            STA !SpriteLoadTable,x
    ++
            LDX $15E9|!addr
    RTS
    +
    	LDA !ScratchB
    	BNE +
    	LDA !FrameIndex,x					;\
    	CMP !LastFrameIndex,x				;|if last frame is different to new frame then
    	BNE +								;|do dynamic routine
    RTS										;/
    +
    RTS
    

    Cluster:

    DynamicRoutine:
    	%CheckEvenOrOdd("DZ_DS_Loc_US_Cluster")
    	BEQ +								;/
    RTS
    +
    	%FindSpace("DZ_DS_Loc_US_Cluster,x")
    	BCS +
    
            PHX
    	LDA.l DZ_DS_Loc_US_Cluster,x
    	TAX
    
    	LDA.l DZ_DS_Loc_IsValid,x
    	BNE ++
            STZ !ClusterSpriteNumber,x
    ++
            PLX
    RTS
    +
    	LDA !ScratchB
    	BNE +
    	LDA !FrameIndex,x					;\
    	CMP !LastFrameIndex,x				;|if last frame is different to new frame then
    	BNE +								;|do dynamic routine
    RTS										;/
    +
    RTS
    
  11. Lo siguiente es rellenar los parametros para llamar a la macro %DynamicRoutine, Para esto nos ayudaremos de algunas Scratch RAM, lo primero es rellenar el offset y el size: Normal:
    DynamicRoutine:
    	%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
    	BEQ +								;/
    RTS
    +
    	%FindSpace("DZ_DS_Loc_US_Normal,x")
    	BCS +
    
    	LDA.l DZ_DS_Loc_US_Normal,x
    	TAX
    
    	LDA.l DZ_DS_Loc_IsValid,x
    	BNE ++
            LDX $15E9|!addr
            STZ !SpriteStatus,x
            LDA !SpriteLoadStatus,x
            TAX
            LDA #$00
            STA !SpriteLoadTable,x
    ++
            LDX $15E9|!addr
    RTS
    +
    	LDA !ScratchB
    	BNE +
    	LDA !FrameIndex,x					;\
    	CMP !LastFrameIndex,x				;|if last frame is different to new frame then
    	BNE +								;|do dynamic routine
    RTS										;/
    +
    	LDA #$00
    	XBA
    	LDA !FrameIndex,x
    	REP #$30
    	ASL
    	TAY
    	PHY
    	SEP #$20
    	LDA ResourceSize,y
    	STA !Scratch0
    	REP #$20
    	TYA
    	ASL
    	TAY
    	PHY
    	LDA ResourceOffset,y
    	STA !Scratch1
    	SEP #$30
    RTS
    

    Cluster:

    DynamicRoutine:
    	%CheckEvenOrOdd("DZ_DS_Loc_US_Cluster")
    	BEQ +								;/
    RTS
    +
    	%FindSpace("DZ_DS_Loc_US_Cluster,x")
    	BCS +
    
            PHX
    	LDA.l DZ_DS_Loc_US_Cluster,x
    	TAX
    
    	LDA.l DZ_DS_Loc_IsValid,x
    	BNE ++
            STZ !ClusterSpriteNumber,x
    ++
            PLX
    RTS
    +
    	LDA !ScratchB
    	BNE +
    	LDA !FrameIndex,x					;\
    	CMP !LastFrameIndex,x				;|if last frame is different to new frame then
    	BNE +								;|do dynamic routine
    RTS										;/
    +
            PHX
    	LDA #$00
    	XBA
    	LDA !FrameIndex,x
    	REP #$30
    	ASL
    	TAY
    	PHY
    	SEP #$20
    	LDA ResourceSize,y
    	STA !Scratch0
    	REP #$20
    	TYA
    	ASL
    	TAY
    	PHY
    	LDA ResourceOffset,y
    	STA !Scratch1
    	SEP #$30
    RTS
    

    En este caso agregue además los PHY para usar esos valores despues y ahorrar algunos ciclos.

  12. Actualizamos el !LastFrameIndex, conseguimos el offset, ponemos al sprite como valido y llamamos la macro %DynamicRoutine, apoyandono en los Defines generados por el DRAdder. Normal:
    DynamicRoutine:
    	%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
    	BEQ +								;/
    RTS
    +
    	%FindSpace("DZ_DS_Loc_US_Normal,x")
    	BCS +
    
    	LDA.l DZ_DS_Loc_US_Normal,x
    	TAX
    
    	LDA.l DZ_DS_Loc_IsValid,x
    	BNE ++
            LDX $15E9|!addr
            STZ !SpriteStatus,x
            LDA !SpriteLoadStatus,x
            TAX
            LDA #$00
            STA !SpriteLoadTable,x
    ++
            LDX $15E9|!addr
    RTS
    +
    	LDA !ScratchB
    	BNE +
    	LDA !FrameIndex,x					;\
    	CMP !LastFrameIndex,x				;|if last frame is different to new frame then
    	BNE +								;|do dynamic routine
    RTS										;/
    +
    	LDA #$00
    	XBA
    	LDA !FrameIndex,x
    	REP #$30
    	ASL
    	TAY
    	PHY
    	SEP #$20
    	LDA ResourceSize,y
    	STA !Scratch0
    	REP #$20
    	TYA
    	ASL
    	TAY
    	PHY
    	LDA ResourceOffset,y
    	STA !Scratch1
    	SEP #$30
    
    	LDA !FrameIndex,x
    	STA !LastFrameIndex,x
    	TAY
    
    
    	%GetVramDispDynamicRoutine(DZ_DS_Loc_US_Normal)
    	STA !ScratchD
    
    	LDA.l DZ_DS_Loc_US_Normal,x
    	TAX
    
    	LDA #$01
    	STA.l DZ_DS_Loc_IsValid,x
    
    	%DynamicRoutine(!ScratchD, #!GFX00, #!GFX00>>16, !Scratch1, !Scratch0)
    RTS
    

    Cluster:

    DynamicRoutine:
    	%CheckEvenOrOdd("DZ_DS_Loc_US_Cluster")
    	BEQ +								;/
    RTS
    +
    	%FindSpace("DZ_DS_Loc_US_Cluster,x")
    	BCS +
    
            PHX
    	LDA.l DZ_DS_Loc_US_Cluster,x
    	TAX
    
    	LDA.l DZ_DS_Loc_IsValid,x
    	BNE ++
            STZ !ClusterSpriteNumber,x
    ++
            PLX
    RTS
    +
    	LDA !ScratchB
    	BNE +
    	LDA !FrameIndex,x					;\
    	CMP !LastFrameIndex,x				;|if last frame is different to new frame then
    	BNE +								;|do dynamic routine
    RTS										;/
    +
            PHX
    	LDA #$00
    	XBA
    	LDA !FrameIndex,x
    	REP #$30
    	ASL
    	TAY
    	PHY
    	SEP #$20
    	LDA ResourceSize,y
    	STA !Scratch0
    	REP #$20
    	TYA
    	ASL
    	TAY
    	PHY
    	LDA ResourceOffset,y
    	STA !Scratch1
    	SEP #$30
    
    	LDA !FrameIndex,x
    	STA !LastFrameIndex,x
    	TAY
    
    
    	%GetVramDispDynamicRoutine(DZ_DS_Loc_US_Cluster)
    	STA !ScratchD
    
    	LDA.l DZ_DS_Loc_US_Cluster,x
    	TAX
    
    	LDA #$01
    	STA.l DZ_DS_Loc_IsValid,x
    
    	%DynamicRoutine(!ScratchD, #!GFX00, #!GFX00>>16, !Scratch1, !Scratch0)
    RTS
    
  13. Ahora se debe hacer lo mismo con la ultima linea, pero varios de esos valores, ya los tenemos, ya que, se usaron en la llamada anterior. Normal:
    DynamicRoutine:
    	%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
    	BEQ +								;/
    RTS
    +
    	%FindSpace("DZ_DS_Loc_US_Normal,x")
    	BCS +
    
    	LDA.l DZ_DS_Loc_US_Normal,x
    	TAX
    
    	LDA.l DZ_DS_Loc_IsValid,x
    	BNE ++
            LDX $15E9|!addr
            STZ !SpriteStatus,x
            LDA !SpriteLoadStatus,x
            TAX
            LDA #$00
            STA !SpriteLoadTable,x
    ++
            LDX $15E9|!addr
    RTS
    +
    	LDA !ScratchB
    	BNE +
    	LDA !FrameIndex,x					;\
    	CMP !LastFrameIndex,x				;|if last frame is different to new frame then
    	BNE +								;|do dynamic routine
    RTS										;/
    +
    	LDA #$00
    	XBA
    	LDA !FrameIndex,x
    	REP #$30
    	ASL
    	TAY
    	PHY
    	SEP #$20
    	LDA ResourceSize,y
    	STA !Scratch0
    	REP #$20
    	TYA
    	ASL
    	TAY
    	PHY
    	LDA ResourceOffset,y
    	STA !Scratch1
    	SEP #$30
    
    	LDA !FrameIndex,x
    	STA !LastFrameIndex,x
    	TAY
    
    
    	%GetVramDispDynamicRoutine(DZ_DS_Loc_US_Normal)
    	STA !ScratchD
    
    	LDA.l DZ_DS_Loc_US_Normal,x
    	TAX
    
    	LDA #$01
    	STA.l DZ_DS_Loc_IsValid,x
    
    	%DynamicRoutine(!ScratchD, #!GFX00, #!GFX00>>16, !Scratch1, !Scratch0)
    
    	REP #$30
    	PLY
    	LDA ResourceOffset+2,y
    	STA !Scratch1
    	PLY
    	SEP #$20
    	LDA ResourceSize+1,y
    	STA !Scratch0
    	SEP #$10
    RTS
    

    Cluster:

    DynamicRoutine:
    	%CheckEvenOrOdd("DZ_DS_Loc_US_Cluster")
    	BEQ +								;/
    RTS
    +
    	%FindSpace("DZ_DS_Loc_US_Cluster,x")
    	BCS +
    
            PHX
    	LDA.l DZ_DS_Loc_US_Cluster,x
    	TAX
    
    	LDA.l DZ_DS_Loc_IsValid,x
    	BNE ++
            STZ !ClusterSpriteNumber,x
    ++
            PLX
    RTS
    +
    	LDA !ScratchB
    	BNE +
    	LDA !FrameIndex,x					;\
    	CMP !LastFrameIndex,x				;|if last frame is different to new frame then
    	BNE +								;|do dynamic routine
    RTS										;/
    +
            PHX
    	LDA #$00
    	XBA
    	LDA !FrameIndex,x
    	REP #$30
    	ASL
    	TAY
    	PHY
    	SEP #$20
    	LDA ResourceSize,y
    	STA !Scratch0
    	REP #$20
    	TYA
    	ASL
    	TAY
    	PHY
    	LDA ResourceOffset,y
    	STA !Scratch1
    	SEP #$30
    
    	LDA !FrameIndex,x
    	STA !LastFrameIndex,x
    	TAY
    
    
    	%GetVramDispDynamicRoutine(DZ_DS_Loc_US_Cluster)
    	STA !ScratchD
    
    	LDA.l DZ_DS_Loc_US_Cluster,x
    	TAX
    
    	LDA #$01
    	STA.l DZ_DS_Loc_IsValid,x
    
    	%DynamicRoutine(!ScratchD, #!GFX00, #!GFX00>>16, !Scratch1, !Scratch0)
    
    	REP #$30
    	PLY
    	LDA ResourceOffset+2,y
    	STA !Scratch1
    	PLY
    	SEP #$20
    	LDA ResourceSize+1,y
    	STA !Scratch0
    	SEP #$10
    RTS
    
  14. El VRAMDisp, debe sumarsele 0x10 por cada linea que tenga el sprite - 1, por ejemplo si el sprite usa 2 lineas de 8 pixeles de alto, entonces el valor seria 0x10, si usara 4, seria 0x30, si usara 6 seria 0x50, etc. En mi caso Klump usa 4 lineas. Luego de esto se llama a la macro %DynamicRoutine. Importante: si la segunda linea tiene Size igual a 0, entonces no debe realizarse la rutina, para esto añadimos un chequeo. Normal:
    DynamicRoutine:
    	%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
    	BEQ +								;/
    RTS
    +
    	%FindSpace("DZ_DS_Loc_US_Normal,x")
    	BCS +
    
    	LDA.l DZ_DS_Loc_US_Normal,x
    	TAX
    
    	LDA.l DZ_DS_Loc_IsValid,x
    	BNE ++
            LDX $15E9|!addr
            STZ !SpriteStatus,x
            LDA !SpriteLoadStatus,x
            TAX
            LDA #$00
            STA !SpriteLoadTable,x
    ++
            LDX $15E9|!addr
    RTS
    +
    	LDA !ScratchB
    	BNE +
    	LDA !FrameIndex,x					;\
    	CMP !LastFrameIndex,x				;|if last frame is different to new frame then
    	BNE +								;|do dynamic routine
    RTS										;/
    +
    	LDA #$00
    	XBA
    	LDA !FrameIndex,x
    	REP #$30
    	ASL
    	TAY
    	PHY
    	SEP #$20
    	LDA ResourceSize,y
    	STA !Scratch0
    	REP #$20
    	TYA
    	ASL
    	TAY
    	PHY
    	LDA ResourceOffset,y
    	STA !Scratch1
    	SEP #$30
    
    	LDA !FrameIndex,x
    	STA !LastFrameIndex,x
    	TAY
    
    
    	%GetVramDispDynamicRoutine(DZ_DS_Loc_US_Normal)
    	STA !ScratchD
    
    	LDA.l DZ_DS_Loc_US_Normal,x
    	TAX
    
    	LDA #$01
    	STA.l DZ_DS_Loc_IsValid,x
    
    	%DynamicRoutine(!ScratchD, #!GFX00, #!GFX00>>16, !Scratch1, !Scratch0)
    
    	REP #$30
    	PLY
    	LDA ResourceOffset+2,y
    	STA !Scratch1
    	PLY
    	SEP #$20
    	LDA ResourceSize+1,y
    	STA !Scratch0
    	SEP #$10
    	BEQ +
    
    	LDA !ScratchD
    	CLC
    	ADC #$10
    	STA !ScratchD
    	%DynamicRoutine(!ScratchD, #!GFX00, #!GFX00>>16, !Scratch1, !Scratch0)
    +
            LDX !SpriteIndex
    RTS
    

    Cluster:

    DynamicRoutine:
    	%CheckEvenOrOdd("DZ_DS_Loc_US_Cluster")
    	BEQ +								;/
    RTS
    +
    	%FindSpace("DZ_DS_Loc_US_Cluster,x")
    	BCS +
    
            PHX
    	LDA.l DZ_DS_Loc_US_Cluster,x
    	TAX
    
    	LDA.l DZ_DS_Loc_IsValid,x
    	BNE ++
            STZ !ClusterSpriteNumber,x
    ++
            PLX
    RTS
    +
    	LDA !ScratchB
    	BNE +
    	LDA !FrameIndex,x					;\
    	CMP !LastFrameIndex,x				;|if last frame is different to new frame then
    	BNE +								;|do dynamic routine
    RTS										;/
    +
            PHX
    	LDA #$00
    	XBA
    	LDA !FrameIndex,x
    	REP #$30
    	ASL
    	TAY
    	PHY
    	SEP #$20
    	LDA ResourceSize,y
    	STA !Scratch0
    	REP #$20
    	TYA
    	ASL
    	TAY
    	PHY
    	LDA ResourceOffset,y
    	STA !Scratch1
    	SEP #$30
    
    	LDA !FrameIndex,x
    	STA !LastFrameIndex,x
    	TAY
    
    
    	%GetVramDispDynamicRoutine(DZ_DS_Loc_US_Cluster)
    	STA !ScratchD
    
    	LDA.l DZ_DS_Loc_US_Cluster,x
    	TAX
    
    	LDA #$01
    	STA.l DZ_DS_Loc_IsValid,x
    
    	%DynamicRoutine(!ScratchD, #!GFX00, #!GFX00>>16, !Scratch1, !Scratch0)
    
    	REP #$30
    	PLY
    	LDA ResourceOffset+2,y
    	STA !Scratch1
    	PLY
    	SEP #$20
    	LDA ResourceSize+1,y
    	STA !Scratch0
    	SEP #$10
    	BEQ +
    
    	LDA !ScratchD
    	CLC
    	ADC #$10
    	STA !ScratchD
    	%DynamicRoutine(!ScratchD, #!GFX00, #!GFX00>>16, !Scratch1, !Scratch0)
    +
            PLX
    RTS
    

Creación de Sprites Dinámicos Con Memoria Compartida

Creación de Sprites Dinámicos Con Memoria Mixta

Creación de Sprites Semi-Dinámicos

Creación de Sprites Dinámicos de 2 fases

Creación de Sprites con Rotación y Re-escalamiento

Uso

Sistema de Cambio de Gráficos y Tilemaps

Cambio de Gráficos del Player

Cambio de Bloques en tiempo real

Sistema de Cambio de Paletas de Colores

Cambio de Paletas de colores del Player

Sistema de Tinte-Saturación-Valor (HSV)

Sistema de Rojo-Verde-Azul (RGB)

Sistema de OAM

Modo 50% más

Widescreen

Optimizaciones de la rutina NMI-Handler

Dynamic Z y Dyzen X

Repositorio

Enlace de acceso al repositorio: https://github.com/weierstrass1/Dynamic-Z

Dynamic Z es un proyecto es de libre uso, solo se pide agregar a anonimzwx en los créditos de los proyectos donde se use. En caso de querer colaborar con el parche pedir permiso a anonimzwx.

Donaciones

Si deseas apoyar este proyecto puedes hacerlo a traves de Patreon: https://www.patreon.com/user?u=27937024

Créditos

El autor original del parche es anonimzwx.

Agradecimientos especiales a: