Dynamic Xtreme UberASM guide
| English | Português | Español | 日本語 |
This article explains how to use Dynamic Xtreme from UberASM to configure a level's resources and change graphics and colors during gameplay. Examples cover restricting palettes, choosing VRAM space for dynamic sprites, creating animations, and changing the player's graphics.
Requirements
- Have UberASMTool installed and configured to insert code into your ROM.
- Have Dynamic Xtreme installed with the features required by your chosen example. See the Quick Start guide for installation.
See the ASM API reference for call parameters and variable usage.
Each example requires the following features:
| Example | Required features |
|---|---|
| Setting aside palettes for other resources | PalettesChange.
|
| Choosing VRAM space for dynamic sprites | GraphicsChange and DynamicPoses. Drawing these poses with Dynamic Xtreme also requires DrawingSystem.
|
| Setting the transfer limit per frame | GraphicsChange for graphics uploads or PalettesChange for palette uploads; both if you use both types.
|
| Creating graphics animations | GraphicsChange and PalettesChange, since the example uploads graphics and a palette. Add PalettesEffects if you set !PaletteGlobalEffect to 1 to use the global effect.
|
| Changing palettes during gameplay | PalettesChange and, with !PaletteGlobalEffect = 1 as in the example, PalettesEffects. To upload without the global effect, use !PaletteGlobalEffect = 0; only PalettesChange is then required.
|
| Changing the player's graphics | PlayerFeatures and its dependencies, GraphicsChange and PalettesChange.
|
| Applying a global palette effect, fixed, cyclic, or transitional | PalettesChange and PalettesEffects.
|
The graphics animation in this guide copies data to a fixed VRAM destination: it does not require DynamicPoses or DrawingSystem. See the API feature table for the routines and macros enabled by each feature.
Preparing the examples
Each example stands on its own. Save the level code in the appropriate UberASMTool folder and add it to the list. If you combine examples in one file, merge their load, init, and main blocks so the labels are not duplicated.
loadprepares data while the level loads.initsets options when entering the level. The palette restriction example uses this block.mainruns during gameplay, once per frame.
Dynamic Xtreme defines and macros must be available in UberASMTool through the integration performed during installation. Declare any additional graphics, palettes, or effects in the ASM using the corresponding includes:
;#incdyn "DynamicXtreme/MyResources.dynamicinfo" ;#incpale "DynamicXtreme/MyEffect.paleffect"
Paths are relative to the ASM file. Include only files used by your example. You can also use Dynamic Xtreme's traditional folders, as explained in Files and formats.
After adding or modifying these resources, install Dynamic Xtreme first, then UberASMTool. If you only change the ASM logic, rerun UberASMTool.
Names beginning with RAM_LIBRE_... in these examples are placeholders: replace them with free RAM addresses before inserting the code. They are not Dynamic Xtreme defines. Allocate the specified number of bytes and use separate addresses for variables that must coexist. Check that the game, Dynamic Xtreme, Lunar Magic, and other resources do not use them; an address free in the original SMW may already be in use in your ROM. You can look for addresses in the SMW Central RAM map filtered to empty space.
If you use a short SMW RAM address outside direct page, add |!addr for the appropriate LoROM or SA-1 remapping. For example, when choosing the timer address:
!AnimationTimer = RAM_LIBRE_ANIM_TIMER|!addr
Replace RAM_LIBRE_ANIM_TIMER with your chosen address and keep |!addr. Direct page addresses use the execution context's D base and do not use this remapping. If you choose a full 24-bit address, check its bank and whether the example's instructions can access it; |!addr is intended here for short addresses outside direct page.
Setting aside palettes for other resources
If an animation, HUD, or other resource needs a fixed palette, set it aside so Dynamic Xtreme does not choose it when assigning palettes to dynamic sprites.
Write $FFFE to its DX_Dynamic_Palettes_ID entry. Each entry occupies 2 bytes. For example, palette 9 uses offset $12: 9 × 2.
See Palettes and the global effect in the API for an explanation of this table.
init:
REP #$20
LDA #$FFFE
STA.l DX_Dynamic_Palettes_ID+$12 ; Set aside palette 9
SEP #$20
RTL
To set aside several palettes, write the same value to each entry:
| Palette | Offset |
|---|---|
| 8 | $10
|
| 9 | $12
|
| A | $14
|
| B | $16
|
| C | $18
|
| D | $1A
|
| E | $1C
|
| F | $1E
|
This prevents automatic assignment from claiming the palette; it does not upload colors. A resource configured to use it manually can still change its colors, so do not also assign it to another resource that needs a different palette.
Choosing VRAM space for dynamic sprites
Dynamic sprites need VRAM space for their poses. Choose a region not used by the level's static graphics.
The SMW VRAM map on SNESLab shows the regions corresponding to SP1, SP2, SP3, and SP4.
For example, to allow SP4:
DynamicPoseSpaceConfig takes one of the predefined configurations.
init:
LDA #!DynamicSpaceConfigSP4
JSL !DynamicPoseSpaceConfig
RTL
If you need more space and the level allows it, choose !DynamicSpaceConfigSP34 for SP3 and SP4, or !DynamicSpaceConfigSP234 for SP2, SP3, and SP4. Other options include half pages and configurations that share space with the player or DSX.
The available defines are listed in DXDefines.asm and the API configuration table. Set the configuration before the level starts loading poses; changing it does not automatically remove poses already in VRAM.
Creating a custom configuration
Use DefineDynamicSpaceConfig to combine regions differently. This example makes the first half of SP3 and all of SP4 available, restricting everything else:
init:
; Restrict SP1 and SP2.
%DefineDynamicSpaceConfig($00, $3F, $80)
; Make the first half of SP3 available.
%DefineDynamicSpaceConfig($40, $4F, $00)
; Restrict the second half of SP3.
%DefineDynamicSpaceConfig($50, $5F, $80)
; Make SP4 available.
%DefineDynamicSpaceConfig($60, $7F, $00)
RTL
The first two parameters are the first and last 16×16 blocks, both inclusive. The third is $00 to allow dynamic poses or $80 to restrict the region. IDs run left to right and top to bottom, with 8 blocks per row. The macro documentation includes the full numbering table.
This replaces the preset configuration call; do not run both configurations in sequence. Adjust the ranges to cover blocks $00–$7F without overlaps, and restrict regions needed by other level graphics. Like the previous example, this requires GraphicsChange and DynamicPoses.
Setting the transfer limit per frame
DX_Dynamic_MaxDataPerFrame sets how many bytes normal Dynamic Xtreme uploads may schedule in a frame. Dynamic poses, animations, and other transfers using its macros share this limit.
See these variables in Counters and data per frame in the API.
The default is $0800, or 2048 bytes. This example increases it to $0A00, or 2560 bytes:
init:
REP #$20
LDA #$0A00
STA.l DX_Dynamic_MaxDataPerFrame
SEP #$20
RTL
Increasing it allows more uploads to be requested in one frame, but does not give the SNES more time to perform them. Too much data can cause visual problems. Adjust the value for your level and test with its sprites and animations running together.
Normal macros return carry clear if an upload does not fit within the limit. Keep the current graphics or colors and retry later. Forced variants skip the check and still add their bytes to the total.
Creating graphics animations
This example creates a graphics animation: a table gives each image's starting address, a timer controls when to change images, and the previous image stays in place if the transfer is rejected.
It uses TransferToVRAM for graphics changes, its Forced variant for the initial upload, and TransferPaletteResource for colors. Follow those links for parameters, registers, and results.
Declare the example's files in DynamicXtreme/SMMArrow.dynamicinfo:
Resources:
SMMArrow.bin
Palettes:
SMMArrowPal.bin
;#incdyn "DynamicXtreme/SMMArrow.dynamicinfo"
; Choose free RAM here: one separate byte per variable.
!CurrentPoseIndex = RAM_LIBRE_ANIM_POSE
!AnimationTimer = RAM_LIBRE_ANIM_TIMER
!PaletteLoaded = RAM_LIBRE_ANIM_PALETA
; Frames to wait before attempting the next change.
!TimePerPose = $03
; Bytes per image: one 8x8 4bpp tile occupies $0020.
!DataSend = $0200
; Number of images used.
!NumberOfPoses = $05
; Source: installed resource. You can also use !AN2Buffer2.
!Source = !ResourceSMMArrow
; VRAM destination address.
!Destination = !BG2Line0
; Installed palette and first destination color.
!Palette = "SMMArrowPal"
!PaletteDestination = $71
; 0 = no global effect; 1 = apply the global effect.
!PaletteGlobalEffect = 0
init:
STZ !CurrentPoseIndex
LDA #!TimePerPose
STA !AnimationTimer
STZ !PaletteLoaded
%ForcedTransferToVRAM(#!Destination, #!Source, #((!Source>>16)&$FF), #!DataSend)
%TransferPaletteResource(!Palette, #!PaletteDestination, !PaletteGlobalEffect)
BCC .Return
LDA #$01
STA !PaletteLoaded
.Return
RTL
main:
; Retry the palette if init's upload was rejected.
LDA !PaletteLoaded
BNE .Animate
%TransferPaletteResource(!Palette, #!PaletteDestination, !PaletteGlobalEffect)
BCS .PaletteReady
RTL
.PaletteReady
LDA #$01
STA !PaletteLoaded
.Animate
LDA !AnimationTimer
BEQ .NextPose
DEC !AnimationTimer
RTL
.NextPose
LDA !CurrentPoseIndex
INC A
CMP #!NumberOfPoses
BCC .SelectPose
LDA #$00
.SelectPose
STA $05
ASL
TAX
REP #$20
LDA.l PosesTable,x
CLC
ADC.w #!Source
STA $00
SEP #$20
%TransferToVRAM(#!Destination, $00, #((!Source>>16)&$FF), #!DataSend)
BCC .Return
LDA $05
STA !CurrentPoseIndex
LDA #!TimePerPose
STA !AnimationTimer
.Return
RTL
PosesTable:
dw $0000
dw !DataSend*$01
dw !DataSend*$02
dw !DataSend*$03
dw !DataSend*$04
What you can customize
!TimePerPose: wait time. With$03, a change is attempted every four frames: three to count down the timer and one to make the change.!DataSend: bytes per image.$0200is 16 8×8 tiles in 4bpp.!NumberOfPosesandPosesTable: image count and offsets within the source. The example uses the first five images inSMMArrow.bin. Extend the table if you add more.!Source: graphics address. The example reads!ResourceSMMArrowdirectly; use!AN2Buffer2to animate graphics already prepared by Lunar Magic.!Destination: VRAM region to update. Place tiles in the level that use this region, and check that another resource does not overwrite it.!Palette,!PaletteDestination, and!PaletteGlobalEffect: palette resource, first destination color, and participation in the global effect.
Replace the three RAM placeholders at the start with addresses available throughout the level: each variable needs its own byte. These are not temporary scratch bytes. !PaletteLoaded lets the code retry a rejected palette upload without repeating it once accepted.
!ResourceSMMArrow, !PaletteSMMArrowPal, and !PaletteSMMArrowPalSize are generated when installing the DynamicInfo and written to DXDefines.asm. Their names are normalized to PascalCase by removing separators; here, the capitalization in SMMArrow and SMMArrowPal is preserved.
All images used must be in the same source bank. The initial graphics upload uses Forced; later changes respect the frame's data limit. If an upload is rejected, the code keeps the previous image and retries next frame without advancing the index or resetting the timer.
The timer advances each time main runs. To stop the animation while paused or while the action is frozen, check $13D4|!addr and $9D before counting it down.
Changing palettes during gameplay
TransferPalette macros upload colors while keeping SMW's and Dynamic Xtreme's palette copies up to date. They also prepare palette effects, so you do not need to copy buffers or clear flags manually.
This example uses TransferPaletteResource; see its API entry for the parameters and carry results.
The example uses SMMArrowPal.bin, a 15-color file ($001E bytes), and changes colors 1–F of palette 7. Color 0 is left unchanged.
In DynamicXtreme/SMMArrow.dynamicinfo, declare the graphics as a general resource and the palette under Palettes:
Resources:
SMMArrow.bin
Palettes:
SMMArrowPal.bin
In the level ASM:
;#incdyn "DynamicXtreme/SMMArrow.dynamicinfo"
; Choose one byte of free RAM here.
!PaletteChangePending = RAM_LIBRE_CAMBIO_PALETA
!Palette = "SMMArrowPal"
!PaletteDestination = $71
!PaletteGlobalEffect = 1
init:
LDA #$01
STA !PaletteChangePending
; Continue to the first upload attempt.
main:
LDA !PaletteChangePending
BNE .TryTransfer
RTL
.TryTransfer
%TransferPaletteResource(!Palette, #!PaletteDestination, !PaletteGlobalEffect)
BCC .Return
; Confirm the change only if the call was accepted.
STZ !PaletteChangePending
.Return
RTL
Replace RAM_LIBRE_CAMBIO_PALETA with one byte of free RAM available throughout the level. !PaletteChangePending is set to 1 when a change is requested and returns to 0 only when the macro returns carry set. If the call fails, the change remains pending and is retried next frame. Once accepted, the upload is not repeated.
!Palette selects the resource, and !PaletteDestination selects the first destination color. !PaletteGlobalEffect, the macro's last parameter, controls the upload:
1: prepares the original colors so they receive the global effect before being displayed. Also prepares the palette if no effect has been selected yet.0: uploads the colors without the global effect and excludes that palette from it. Also updates SMW's buffers, which prevents palette corruption during the goal victory animation.
To upload the same palette without the global effect, change only this define and keep the carry check:
!PaletteGlobalEffect = 0
If effects are disabled in the installation, both options upload directly. When effects are enabled and you use 1, the system uploads the processed result; it does not display an unaffected palette for one frame first.
SMMArrowPal uses !PaletteSMMArrowPal and !PaletteSMMArrowPalSize, generated from the DynamicInfo's Palettes section and written to DXDefines.asm. Names are normalized to PascalCase by removing separators and capitalizing each word. In this case, SMMArrowPal stays as written.
To request another change during gameplay, set !PaletteChangePending to 1 when your chosen condition is met. Do not set it every frame if the palette is unchanged. For a direct upload, carry set means the transfer was scheduled; with the global effect, it means the original colors were prepared, not that the result is already visible.
The effect option applies to the entire 16-color palette. When uploading colors for a global effect, prepare all visible colors, as with the 15 in this example; do not change just a few colors expecting to preserve a base you have not prepared yet.
If you use a fixed sprite palette from 8–F, set it aside first as described at the start of this article. These macros do not perform automatic assignment. To upload from an address, pointer, or table, see Uploading colors with TransferPalette in the API.
Changing the player's graphics
This example switches between the original player and Geno when you press up. Its defines let you choose the graphics and four palettes: Mario, Luigi, Fire Mario, and Fire Luigi. It also updates the selected palette when the player's state changes.
Requires PlayerFeatures. Custom graphics must use the same pose layout as the originals; changing the source does not adapt a spritesheet with a different layout.
The code uses the player variables and macros DivW, TransferPaletteFromPointer, and TransferPaletteFromAddress. Their API entries explain each call's parameters, registers, and results.
Declare the following in DynamicXtreme/Geno.dynamicinfo:
Resources:
Geno.bin
Palettes:
GenoPal.bin
GenoPalPlayer2.bin
FireGenoPal.bin
FireGenoPalPlayer2.bin
;#incdyn "DynamicXtreme/Geno.dynamicinfo"
; Choose two consecutive bytes of free RAM here.
!Last0D82 = RAM_LIBRE_JUGADOR_2_BYTES
; Custom player graphics.
!PlayerGFXResource = !ResourceGeno
; Palettes: player 1, player 2, and their fire variants.
!PlayerGFXPalette = !PaletteGenoPal
!PlayerGFXPalettePlayer2 = !PaletteGenoPalPlayer2
!PlayerGFXFirePalette = !PaletteFireGenoPal
!PlayerGFXFirePalettePlayer2 = !PaletteFireGenoPalPlayer2
; Switch button: $08 = up.
!ChangeButton = $08
init:
REP #$20
LDA $0D82|!addr
STA !Last0D82
SEP #$20
main:
LDA !ButtonDown_BYETUDLR
AND #!ChangeButton
BNE .Change
REP #$20
LDA $0D82|!addr
CMP !Last0D82
BNE .Reload
SEP #$20
RTL
.Change
LDA.l DX_Dynamic_Player_GFX_BNK
CMP.b #!GFX32Buffer>>16
BEQ .ChooseGeno
JMP .Mario
.ChooseGeno
JMP .Geno
.Reload
SEP #$20
LDA.l DX_Dynamic_Player_GFX_BNK
CMP.b #!GFX32Buffer>>16
BNE .Geno
JMP .Mario
.Geno
; Get 0, 1, 2, or 3 based on the player and fire state.
REP #$20
LDA $0D82|!addr
SEC
SBC #!PlayerBasePalette
STA $00
SEP #$20
%DivW($01, $00, #$14)
; Each address in GenoPals occupies three bytes.
LDA !DivisionResult
ASL
CLC
ADC !DivisionResult
TAX
; Keep the selected source and prepare colors 2-5.
REP #$20
LDA.l GenoPals,x
STA $03
CLC
ADC #$0002
STA $00
SEP #$20
LDA.l GenoPals+2,x
STA $02
STA $05
%TransferPaletteFromPointer($00, #$82, $0008, 0)
BCS .ApplyGeno
RTL
.ApplyGeno
REP #$20
LDA.w #!PlayerGFXResource
STA.l DX_Dynamic_Player_GFX_Addr
LDA.w #(!PlayerGFXResource>>16)&$00FF
STA.l DX_Dynamic_Player_GFX_BNK
; The player routine uploads colors 6-F.
LDA $03
CLC
ADC #$000A
STA.l DX_Dynamic_Player_Palette_Addr
SEP #$20
LDA $05
STA.l DX_Dynamic_Player_Palette_BNK
JMP .ConfirmSelection
.Mario
%TransferPaletteFromAddress(!PlayerPaletteColor2To5, #$82, $0008, 0)
BCS .ApplyMario
RTL
.ApplyMario
REP #$20
LDA.w #!GFX32Buffer
STA.l DX_Dynamic_Player_GFX_Addr
LDA $0D82|!addr
STA.l DX_Dynamic_Player_Palette_Addr
LDA.w #(!GFX32Buffer>>16)&$00FF
STA.l DX_Dynamic_Player_GFX_BNK
LDA #$0000
STA.l DX_Dynamic_Player_Palette_BNK
SEP #$20
.ConfirmSelection
REP #$20
LDA $0D82|!addr
STA !Last0D82
SEP #$20
RTL
GenoPals:
dl !PlayerGFXPalette
dl !PlayerGFXPalettePlayer2
dl !PlayerGFXFirePalette
dl !PlayerGFXFirePalettePlayer2
What you can customize
!PlayerGFXResource: custom character graphics.- The four
!PlayerGFX...Palette...defines: palette addresses, in the order used byGenoPals. !ChangeButton: button used to switch characters. Uses the controller bitsBYETUDLR;$08is up.!Last0D82: replaceRAM_LIBRE_JUGADOR_2_BYTESwith two consecutive free RAM bytes. This stores the game's palette selection; neither byte may be used by other code.
!ResourceGeno, !PaletteGenoPal, !PaletteGenoPalPlayer2, !PaletteFireGenoPal, and !PaletteFireGenoPalPlayer2 are generated from the DynamicInfo and written to DXDefines.asm. Names use PascalCase, with spaces, hyphens, and underscores removed and existing capitals preserved.
Each palette file contains colors 1–F. Its first color is skipped to copy colors 2–5; the final ten start at +$0A and are used by the player routine.
Colors 2–5 are uploaded with TransferPalette, which also updates SMW's buffers. The example does not confirm a graphics change if that transfer is rejected. A pending player-state change is retried while it differs from !Last0D82; if a button-requested change is rejected, press the button again.
The code identifies the original player by the bank of !GFX32Buffer. If you adapt it to switch between several custom characters in ROM, you will need another way to track the selected character.
This example uploads the player's palette without the global effect. Changing a four-color upload's parameter to 1 is not enough to add an effect: you must prepare all visible colors of palette 8 and account for its special upload handling.
Applying a global palette effect
These examples apply a fixed effect, cycle through effects, or run a transition that stops when finished. You can select each palette separately and include the fixed color. The cycle and transition examples also let you set the speed with !Delay.
All three configure the global effect variables and the fixed color and first palette color variables. Their API entries explain each variable.
In *.paleffect files, EffectType selects the effect type. Ratios range from 0 to 127, and Channel values range from 0 to 31.
Choosing the affected palettes
In each example, set !P0 through !PF to 1 to include the corresponding palette or 0 to exclude it. !EnableFixedColor controls the fixed color, and !EnableColor00 includes color 0,0.
The masks !EnableBG, !EnableSP, and !EnableColors are calculated from these settings. You do not need to edit them manually. In the current API, the lowest bit corresponds to the first palette in each group:
| Bit value | Background palette | Sprite palette |
|---|---|---|
$01 |
0 | 8 |
$02 |
1 | 9 |
$04 |
2 | A |
$08 |
3 | B |
$10 |
4 | C |
$20 |
5 | D |
$40 |
6 | E |
$80 |
7 | F |
The fixed color and color 0,0 use DX_PPU_ColorEffect_Enable: bits $01 and $02, respectively.
For sprites with dynamic assignment, also configure the E bit in PaletteOption. When using TransferPalette, its last parameter controls whether those palettes participate in the global effect. Avoid having other code overwrite the masks without accounting for this configuration.
Applying a fixed effect
This example applies the effect selected by !PaletteEffetID. The configuration shown enables all palettes and the fixed color, and selects the last effect in DecreaseSaturation.
Save the following in DynamicXtreme/DecreaseSaturation.paleffect:
{
"Effects": [
{"Channel2": 0, "Ratio2": 4, "EffectType": 11},
{"Channel2": 0, "Ratio2": 8, "EffectType": 11},
{"Channel2": 0, "Ratio2": 12, "EffectType": 11},
{"Channel2": 0, "Ratio2": 16, "EffectType": 11},
{"Channel2": 0, "Ratio2": 20, "EffectType": 11},
{"Channel2": 0, "Ratio2": 25, "EffectType": 11},
{"Channel2": 0, "Ratio2": 29, "EffectType": 11},
{"Channel2": 0, "Ratio2": 33, "EffectType": 11},
{"Channel2": 0, "Ratio2": 37, "EffectType": 11},
{"Channel2": 0, "Ratio2": 41, "EffectType": 11},
{"Channel2": 0, "Ratio2": 45, "EffectType": 11},
{"Channel2": 0, "Ratio2": 49, "EffectType": 11},
{"Channel2": 0, "Ratio2": 53, "EffectType": 11},
{"Channel2": 0, "Ratio2": 57, "EffectType": 11},
{"Channel2": 0, "Ratio2": 61, "EffectType": 11},
{"Channel2": 0, "Ratio2": 66, "EffectType": 11},
{"Channel2": 0, "Ratio2": 70, "EffectType": 11},
{"Channel2": 0, "Ratio2": 74, "EffectType": 11},
{"Channel2": 0, "Ratio2": 78, "EffectType": 11},
{"Channel2": 0, "Ratio2": 82, "EffectType": 11},
{"Channel2": 0, "Ratio2": 86, "EffectType": 11},
{"Channel2": 0, "Ratio2": 90, "EffectType": 11},
{"Channel2": 0, "Ratio2": 94, "EffectType": 11},
{"Channel2": 0, "Ratio2": 98, "EffectType": 11},
{"Channel2": 0, "Ratio2": 102, "EffectType": 11},
{"Channel2": 0, "Ratio2": 107, "EffectType": 11},
{"Channel2": 0, "Ratio2": 111, "EffectType": 11},
{"Channel2": 0, "Ratio2": 115, "EffectType": 11},
{"Channel2": 0, "Ratio2": 119, "EffectType": 11},
{"Channel2": 0, "Ratio2": 123, "EffectType": 11},
{"Channel2": 0, "Ratio2": 127, "EffectType": 11},
{"Channel2": 0, "Ratio2": 127, "EffectType": 11}
]
}
The file defines 32 effects. EffectType = 11 selects MixS; Channel2 = 0 reduces saturation, and Ratio2 controls how much. The last effect uses 127, applying the full change.
The ASM code is:
;#incpale "DynamicXtreme/DecreaseSaturation.paleffect"
; 1 = include the palette in the effect; 0 = exclude it.
!P0 = 1
!P1 = 1
!P2 = 1
!P3 = 1
!P4 = 1
!P5 = 1
!P6 = 1
!P7 = 1
!P8 = 1
!P9 = 1
!PA = 1
!PB = 1
!PC = 1
!PD = 1
!PE = 1
!PF = 1
!EnableFixedColor = 1
!EnableColor00 = 0
; Effect applied when entering the level.
!PaletteEffetID = !PaletteEffectIDDecreaseSaturation31
; Calculated masks: no manual changes needed.
!EnableBG = !P0|(!P1<<1)|(!P2<<2)|(!P3<<3)|(!P4<<4)|(!P5<<5)|(!P6<<6)|(!P7<<7)
!EnableSP = !P8|(!P9<<1)|(!PA<<2)|(!PB<<3)|(!PC<<4)|(!PD<<5)|(!PE<<6)|(!PF<<7)
!EnableColors = !EnableFixedColor|(!EnableColor00<<1)
init:
LDA #!EnableBG
STA.l DX_Dynamic_Palettes_GlobalBGEnable
LDA #!EnableSP
STA.l DX_Dynamic_Palettes_GlobalSPEnable
LDA #!EnableColors
STA.l DX_PPU_ColorEffect_Enable
REP #$20
LDA #!PaletteEffetID
STA.l DX_Dynamic_Palettes_GlobalEffectID
SEP #$20
RTL
What you can customize
!P0through!PF: affected palettes.!EnableFixedColorand!EnableColor00: whether these colors are affected.!PaletteEffetID: selected effect. You can use any ID generated from your effects file, not just this example's final effect.
Defines !PaletteEffectIDDecreaseSaturation0 through !PaletteEffectIDDecreaseSaturation31 are generated from the *.paleffect and written to DXDefines.asm. Names use PascalCase, with spaces, hyphens, and underscores removed and existing capitals preserved.
The effect is selected in init; you do not need to repeat this every frame. To remove it, write 0 to the global effect ID and leave the palettes enabled so the system restores their original colors:
REP #$20
LDA #$0000
STA.l DX_Dynamic_Palettes_GlobalEffectID
SEP #$20
Cycling through effects
This example runs through a sequence of effects and returns to the first. You can configure the speed, first and last IDs, and affected palettes. The settings shown include only palette 0 and the fixed color.
Save the following in DynamicXtreme/Hue.paleffect:
{
"Effects": [
{"Channel1": 1, "Ratio1": 127, "EffectType": 9},
{"Channel1": 2, "Ratio1": 127, "EffectType": 9},
{"Channel1": 3, "Ratio1": 127, "EffectType": 9},
{"Channel1": 4, "Ratio1": 127, "EffectType": 9},
{"Channel1": 5, "Ratio1": 127, "EffectType": 9},
{"Channel1": 6, "Ratio1": 127, "EffectType": 9},
{"Channel1": 7, "Ratio1": 127, "EffectType": 9},
{"Channel1": 8, "Ratio1": 127, "EffectType": 9},
{"Channel1": 9, "Ratio1": 127, "EffectType": 9},
{"Channel1": 10, "Ratio1": 127, "EffectType": 9},
{"Channel1": 11, "Ratio1": 127, "EffectType": 9},
{"Channel1": 12, "Ratio1": 127, "EffectType": 9},
{"Channel1": 13, "Ratio1": 127, "EffectType": 9},
{"Channel1": 14, "Ratio1": 127, "EffectType": 9},
{"Channel1": 15, "Ratio1": 127, "EffectType": 9},
{"Channel1": 16, "Ratio1": 127, "EffectType": 9},
{"Channel1": 17, "Ratio1": 127, "EffectType": 9},
{"Channel1": 18, "Ratio1": 127, "EffectType": 9},
{"Channel1": 19, "Ratio1": 127, "EffectType": 9},
{"Channel1": 20, "Ratio1": 127, "EffectType": 9},
{"Channel1": 21, "Ratio1": 127, "EffectType": 9},
{"Channel1": 22, "Ratio1": 127, "EffectType": 9},
{"Channel1": 23, "Ratio1": 127, "EffectType": 9},
{"Channel1": 24, "Ratio1": 127, "EffectType": 9},
{"Channel1": 25, "Ratio1": 127, "EffectType": 9},
{"Channel1": 26, "Ratio1": 127, "EffectType": 9},
{"Channel1": 27, "Ratio1": 127, "EffectType": 9},
{"Channel1": 28, "Ratio1": 127, "EffectType": 9},
{"Channel1": 29, "Ratio1": 127, "EffectType": 9},
{"Channel1": 30, "Ratio1": 127, "EffectType": 9},
{"Channel1": 31, "Ratio1": 127, "EffectType": 9},
{"Channel1": 31, "Ratio1": 127, "EffectType": 9}
]
}
The file defines 32 hue effects. EffectType = 9 selects MixH, and Ratio1 = 127 fully applies the selected hue.
;#incpale "DynamicXtreme/Hue.paleffect"
; 1 = include the palette in the effect; 0 = exclude it.
!P0 = 1
!P1 = 0
!P2 = 0
!P3 = 0
!P4 = 0
!P5 = 0
!P6 = 0
!P7 = 0
!P8 = 0
!P9 = 0
!PA = 0
!PB = 0
!PC = 0
!PD = 0
!PE = 0
!PF = 0
!EnableFixedColor = 1
!EnableColor00 = 0
; Allowed values: $00, $01, $03, $07, $0F, $1F, $3F, $7F, and $FF.
!Delay = $03
!StartingPaletteEffetID = !PaletteEffectIDHue0
!EndPaletteEffetID = !PaletteEffectIDHue31
; Calculated masks: no manual changes needed.
!EnableBG = !P0|(!P1<<1)|(!P2<<2)|(!P3<<3)|(!P4<<4)|(!P5<<5)|(!P6<<6)|(!P7<<7)
!EnableSP = !P8|(!P9<<1)|(!PA<<2)|(!PB<<3)|(!PC<<4)|(!PD<<5)|(!PE<<6)|(!PF<<7)
!EnableColors = !EnableFixedColor|(!EnableColor00<<1)
init:
LDA #!EnableBG
STA.l DX_Dynamic_Palettes_GlobalBGEnable
LDA #!EnableSP
STA.l DX_Dynamic_Palettes_GlobalSPEnable
LDA #!EnableColors
STA.l DX_PPU_ColorEffect_Enable
REP #$20
LDA #!StartingPaletteEffetID
STA.l DX_Dynamic_Palettes_GlobalEffectID
SEP #$20
RTL
main:
LDA $14
AND #!Delay
BNE .Return
REP #$20
LDA.l DX_Dynamic_Palettes_GlobalEffectID
CMP #!EndPaletteEffetID
BCS .Restart
INC A
BRA .Store
.Restart
LDA #!StartingPaletteEffetID
.Store
STA.l DX_Dynamic_Palettes_GlobalEffectID
SEP #$20
.Return
RTL
What you can customize
!P0through!PF,!EnableFixedColor, and!EnableColor00: affected colors.!Delay: interval between changes.$00changes every frame,$01every two frames,$03every four, and$07every eight. Use only the values listed in the comment.!StartingPaletteEffetIDand!EndPaletteEffetID: first and last effects in the cycle. They must form a consecutive ID range, with the start less than or equal to the end.
The cycle displays the last ID before returning to the first. It uses $14 to time changes, so the first interval depends on that counter's value when entering the level. To stop it while the action is frozen, also check $9D and $13D4|!addr at the start of main.
Defines !PaletteEffectIDHue0 through !PaletteEffectIDHue31 are generated from the *.paleffect and written to DXDefines.asm. Names are normalized to PascalCase by removing separators; here, Hue stays unchanged.
Running a transition that stops when complete
This example advances from the first effect to the last and keeps the final result. You can configure the affected palettes, fixed color, speed, and ID range.
Save the following in DynamicXtreme/Night.paleffect:
{
"Effects": [
{"Channel1": 0, "Channel2": 0, "Ratio1": 0, "Ratio2": 0, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 4, "Ratio2": 4, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 8, "Ratio2": 8, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 12, "Ratio2": 12, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 16, "Ratio2": 16, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 20, "Ratio2": 20, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 25, "Ratio2": 25, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 29, "Ratio2": 29, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 33, "Ratio2": 33, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 37, "Ratio2": 37, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 41, "Ratio2": 41, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 45, "Ratio2": 45, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 49, "Ratio2": 49, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 53, "Ratio2": 53, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 57, "Ratio2": 57, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 61, "Ratio2": 61, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 66, "Ratio2": 66, "EffectType": 2}
]
}
The transition has 17 steps. EffectType = 2 selects MixRG: it reduces red and green without changing blue. Ratios increase from 0 to 66 to darken the colors gradually without completely removing red and green.
;#incpale "DynamicXtreme/Night.paleffect"
; 1 = include the palette in the effect; 0 = exclude it.
!P0 = 1
!P1 = 1
!P2 = 1
!P3 = 1
!P4 = 1
!P5 = 1
!P6 = 1
!P7 = 1
!P8 = 1
!P9 = 1
!PA = 1
!PB = 1
!PC = 1
!PD = 1
!PE = 1
!PF = 1
!EnableFixedColor = 1
!EnableColor00 = 0
; Allowed values: $00, $01, $03, $07, $0F, $1F, $3F, $7F, and $FF.
!Delay = $03
!StartingPaletteEffetID = !PaletteEffectIDNight0
!EndPaletteEffetID = !PaletteEffectIDNight16
; Calculated masks: no manual changes needed.
!EnableBG = !P0|(!P1<<1)|(!P2<<2)|(!P3<<3)|(!P4<<4)|(!P5<<5)|(!P6<<6)|(!P7<<7)
!EnableSP = !P8|(!P9<<1)|(!PA<<2)|(!PB<<3)|(!PC<<4)|(!PD<<5)|(!PE<<6)|(!PF<<7)
!EnableColors = !EnableFixedColor|(!EnableColor00<<1)
init:
LDA #!EnableBG
STA.l DX_Dynamic_Palettes_GlobalBGEnable
LDA #!EnableSP
STA.l DX_Dynamic_Palettes_GlobalSPEnable
LDA #!EnableColors
STA.l DX_PPU_ColorEffect_Enable
REP #$20
LDA #!StartingPaletteEffetID
STA.l DX_Dynamic_Palettes_GlobalEffectID
SEP #$20
RTL
main:
LDA $14
AND #!Delay
BNE .Return
REP #$20
LDA.l DX_Dynamic_Palettes_GlobalEffectID
CMP #!EndPaletteEffetID
BCS .Finished
INC A
STA.l DX_Dynamic_Palettes_GlobalEffectID
.Finished
SEP #$20
.Return
RTL
What you can customize
!P0through!PF,!EnableFixedColor, and!EnableColor00: affected colors.!Delay: interval between changes, using the same allowed values as the cycle example.!StartingPaletteEffetIDand!EndPaletteEffetID: first and last effects. IDs must be consecutive and in ascending order.
Defines !PaletteEffectIDNight0 through !PaletteEffectIDNight16 are generated from Night.paleffect and written to DXDefines.asm. Names use PascalCase with separators removed; here, Night stays unchanged.
This example moves between mixing effects. To transition toward another complete palette, use PalTransitionRGB or PalTransitionHSL, described in Files and formats.