The wiki has been upgraded to MediaWiki 1.43. The wiki's theme is still being adapted to work with the new version, and may appear broken for the time being. --RanAS

Dynamic Xtreme ASM API

From SnesLab
Jump to: navigation, search
English Português Español 日本語

This article explains how to use Dynamic Xtreme's routines and macros from ASM code. Each entry explains the call's purpose, inputs, and results, with an example.

To learn how to organize a complete sprite, see the sprite creation guide.

Features required to use the API

Available routines, macros, and variables depend on the features selected when installing Dynamic Xtreme. This table shows what to enable for each system:

Feature Available routines and macros Dependencies and details
GraphicsChange VRAM transfers: TransferToVRAM and its FromAddr, Resource, WithLenght, and Forced variants. Provides graphics loading. Also required by DynamicPoses and PlayerFeatures.
DynamicPoses DefineDynamicSpaceConfig, DynamicPoseSpaceConfig, TakeDynamicRequest, DynamicRoutine, SetPropertyAndOffset, !Upload, !UploadWithoutPoseOffset, and the general macros GeneralUpload, Upload, and StandardSpriteUpload. Requires GraphicsChange. Drawing the obtained poses also requires DrawingSystem.
DrawingSystem !DXDraw, !DXDrawWithOffset, and general macros Draw, StandardSpriteDraw, and DrawStatic. Includes the system's drawing and tile validation routines. Dynamic pose drawing also requires DynamicPoses. DrawStatic can draw static poses without the dynamic pose system.
PalettesChange AssignPalette, !PaletteAssignment, !PaletteAssignmentNoUpload, !ReplacePalette, and their general macros; LoadPaletteIDAndAddress; TransferPalette macros and CGRAM transfers. Provides palette assignment and loading. Effects additionally require PalettesEffects.
PalettesEffects SetRGBBase, SetHSLBase, Mix, MixAndMerge, PaletteTransition, DoEffect, and RGB/HSL conversions, including their variants. Also enables ConcatenateRGBPaletteEffectToGlobal and ConcatenateHSLPaletteEffectToGlobal, as sprite aliases or general macros. Requires PalettesChange. Allows global effects through palette assignment or TransferPalette.
PlayerFeatures Customizes player graphics and palettes through the player variables. Does not add a separate family of calls to the categories above. Requires GraphicsChange and PalettesChange.
YoshiFeatures Customizes Yoshi's graphics through the Yoshi variables. Requires PlayerFeatures and its dependencies.
Free7E2000 Adds no calls to this API. Frees and reorganizes the corresponding RAM area. Not an additional requirement for loading or drawing macros. Disabled on SA-1.
ControllerOptimization, FixedColorOptimization, KevinLMVRAMOptimization, and StatusBarOptimization Add no call families to this API. Change how their respective systems work internally. These are not required for pose, drawing, palette, or math macros.
No additional feature Mul, Div, and their W and After... variants. Math macros are included from DXDefines.asm independently of the options above.

PPU macros are included when GraphicsChange or PalettesChange is enabled, but use VRAM transfers with GraphicsChange and CGRAM transfers with PalettesChange. Asar recognizing a macro's name does not mean its required lists and variables are enabled.

To load and draw dynamic poses, enable GraphicsChange, DynamicPoses, and DrawingSystem. Add PalettesChange if the sprite loads palettes and PalettesEffects if it uses effects.

Tool integration

Context Files and calls
PIXI Installation copies general defines to asm/ExtraDefines and shared routines to routines. For sprite aliases, include NormalSpriteDefines.asm, ClusterSpriteDefines.asm, or ExtendedSpriteDefines.asm as appropriate. Each entry explains type-specific differences.
UberASMTool Installation links ExtraDefines/DXDefines.asm from other/macro_library.asm. Use general macros and patch routines with their parameters; aliases such as !Upload belong to PIXI sprite defines files.
GPS Installation links ExtraDefines/DXDefines.asm from defines.asm. Use general macros and patch routines, preparing their inputs from the block's code.

These integrations are prepared for tools configured during installation. If Asar cannot find a name, check the required feature, included files, and, for generated defines, the exact spelling in DXDefines.asm. After changing features or resources, reinstall Dynamic Xtreme and rerun tools using its defines, as explained in the Quick Start guide.

Variables and defines

DXDefines.asm gathers the names Dynamic Xtreme uses for its variables, configurations, resources, and routines. Some represent runtime data; others are constants or generated during resource installation.

General variable names start with DX_ and have no ! prefix, for example DX_Timer. Defines such as !DynamicPoseIDMySprite000 do include !. Keep each name's exact spelling.

Available variables depend on installed features. You do not need to memorize them all: find the category for the system you are using.

General variables

In these tables, size is the number of bytes occupied by one value. Tables also list their entry count. Dynamic Xtreme maintains variables marked internal; normally use API routines to work with them.

Counters and data per frame

Variable Size Purpose
DX_Timer 2 bytes Counts frames in which Dynamic Xtreme runs. Continues during pause and frozen action. Used to track each pose's last use.
DX_SyncTimer 2 bytes Counts frames but stops during pause or frozen action. Useful for animations that should stop with the game.
DX_Dynamic_CurrentDataSend 2 bytes Bytes already scheduled for transfer in the current frame. Updated by loading routines.
DX_Dynamic_MaxDataPerFrame 2 bytes Maximum bytes normal uploads can schedule per frame. Forced variants can exceed it.

Poses loaded in VRAM

These internal cache tables track which poses are loaded, where they are, and which remain in use. Request poses with !Upload or !TakeDynamicRequest.

Variable Size Purpose
DX_Dynamic_Pose_Length 1 byte Number of poses currently registered in the cache.
DX_Dynamic_Pose_HashSize 128 one-byte values Helps find poses by ID.
DX_Dynamic_Pose_ID 128 two-byte values ID of each registered pose.
DX_Dynamic_Pose_Offset 128 one-byte values Location of each pose's graphics in dynamic VRAM space, measured in 16×16 blocks.
DX_Dynamic_Pose_TimeLastUse 128 two-byte values DX_Timer at each pose's last use. Prevents replacement of graphics still being displayed.
DX_Dynamic_Tile_Size / DX_Dynamic_Tile_Pose 128 shared bytes Describe free and occupied VRAM regions. Two names for the same memory.
DX_Dynamic_Tile_Offset 128 one-byte values Helps traverse free and occupied VRAM regions.

Palettes and the global effect

The 16-palette tables use palette order 0–F. In two-byte tables, palette 1 starts at +2, palette 2 at +4, and so on.

Variable Size Purpose
DX_Dynamic_Palettes_GlobalBGEnable 1 byte Each bit determines whether one of palettes 0–7 receives the global effect.
DX_Dynamic_Palettes_GlobalSPEnable 1 byte Each bit determines whether one of sprite palettes 8–F receives the global effect.
DX_Dynamic_Palettes_GlobalEffectID 2 bytes Desired global effect ID. Zero means no global effect.
DX_Dynamic_Palettes_LastGlobalEffectID 2 bytes Last global effect examined by the system, used to detect changes.
DX_Dynamic_Palettes_LastGlobalEffectIDPerPal 16 two-byte values Last global effect calculated per palette. $FFFF: no result yet; $FFFE: update pending, previous result retained.
DX_Dynamic_Palettes_ID 16 two-byte values ID associated with each palette. $FFFF means free; $FFFE excludes it from automatic selection.
DX_Dynamic_Palettes_Updated 16 one-byte values Palettes already updated this frame. Internal.
DX_Dynamic_Palettes_DisableTimer 16 one-byte values A nonzero timer protects a palette from replacement by another automatic assignment. Calls using it reset it to 2.
DX_Dynamic_Palettes_EffectEnabled 1 byte Tracks sprite palettes using effects. Internal palette management state.

To reserve a sprite palette against automatic selection, set its DX_Dynamic_Palettes_ID entry to $FFFE before sprites request palettes.

Color copies and effect results

Variable Size Purpose
DX_PPU_CGRAM_PaletteCopy 512 bytes Working copy of 256 colors at 2 bytes each. Source for SetRGBBaseDefault and SetHSLBaseDefault.
DX_PPU_CGRAM_BaseRGBPalette 768 bytes Original colors split into red, green, and blue: 3 bytes per color.
DX_PPU_CGRAM_BaseHSLPalette 768 bytes Original colors split into hue, saturation, and lightness: 3 bytes per color.
DX_PPU_CGRAM_LocalEffectBuffer 768 bytes Working space for a local effect result before combining it with the global effect.
DX_PPU_CGRAM_LocalEffectID 16 two-byte values Local effect ID applied to each palette.
DX_PPU_CGRAM_PaletteWriteMirror 512 bytes Resulting colors in SNES format, 2 bytes each. AndMergeDefault variants write here.
DX_PPU_CGRAM_BGBaseRGBPaletteLoaded / DX_PPU_CGRAM_SPBaseRGBPaletteLoaded 1 byte each Each bit indicates a background or sprite palette's RGB base is prepared.
DX_PPU_CGRAM_BGBaseHSLPaletteLoaded / DX_PPU_CGRAM_SPBaseHSLPaletteLoaded 1 byte each Same for HSL bases.
DX_PPU_CGRAM_BGEffectLoaded / DX_PPU_CGRAM_SPEffectLoaded 1 byte each Each bit indicates an available effect result for that palette.
DX_PPU_CGRAM_LastPlayerPal 2 bytes Last processed player palette address, compared to detect changes.

Writing colors to a RAM copy does not immediately send them to CGRAM. Use palette routines or a transfer to update the display. The system manages prepared-base and effect flags; they do not need clearing before each call.

Fixed color and the first palette color

The SNES fixed color can be used for color addition and subtraction effects. Color 0,0 is CGRAM's first entry. Dynamic Xtreme can apply the global effect to either independently.

Variable Size Purpose
DX_PPU_ColorEffect_Enable 1 byte Enables the global effect: bit $01 for fixed color and $02 for color 0,0. $03 enables both.
DX_PPU_ColorEffect_BaseLoaded 1 byte Tracks saved original colors, one bit per target: $01 and $02.
DX_PPU_ColorEffect_RGBBaseLoaded / DX_PPU_ColorEffect_HSLBaseLoaded 1 byte each Tracks prepared RGB/HSL colors, using the same bits.
DX_PPU_FixedColor_Mirror 2 bytes Fixed color copy used by the game's normal update.
DX_PPU_FixedColor_Base 2 bytes Original fixed color before effects.
DX_PPU_FixedColor_RGBBase / DX_PPU_FixedColor_HSLBase 3 bytes each Original fixed color split into RGB or HSL.
DX_PPU_FixedColor_Result 2 bytes Fixed color calculated after the effect.
DX_PPU_FixedColor_LastGlobalEffectID 2 bytes Last global effect calculated for fixed color.
DX_PPU_PaletteColor00_Base 2 bytes Original color 0,0.
DX_PPU_PaletteColor00_RGBBase / DX_PPU_PaletteColor00_HSLBase 3 bytes each Original color 0,0 split into RGB or HSL.
DX_PPU_PaletteColor00_Result 2 bytes Desired color 0,0.
DX_PPU_PaletteColor00_LastGlobalEffectID 2 bytes Last global effect calculated for color 0,0.
DX_PPU_PaletteColor00_Update 1 byte Nonzero requests copying Result to color 0,0 during NMI. Reset to 0 afterward.

Pending transfers

These variables store copies to perform during NMI. PPU macros populate them for you. Each copy needs a source, destination, and byte count.

Variable Contents
DX_PPU_VRAM_Transfer_Length Pending graphics copy count, 1 byte.
DX_PPU_VRAM_Transfer_SourceLength Bytes per copy, two-byte values.
DX_PPU_VRAM_Transfer_Offset Each copy's VRAM destination, two-byte values.
DX_PPU_VRAM_Transfer_Source Each copy's source address, two-byte values.
DX_PPU_VRAM_Transfer_SourceBNK Each copy's source bank. The table reserves 2 bytes per entry.
DX_PPU_CGRAM_Transfer_Length Pending color copy count, 1 byte.
DX_PPU_CGRAM_Transfer_SourceLength Bytes per copy, two-byte values.
DX_PPU_CGRAM_Transfer_Offset Each copy's first destination color, one-byte values.
DX_PPU_CGRAM_Transfer_Source Each copy's source address, two-byte values.
DX_PPU_CGRAM_Transfer_SourceBNK Each copy's source bank.

Player and Yoshi

These variables change the sources of player graphics and colors and Yoshi graphics. Graphics files must retain the layout expected by the game routines.

Variable Size Purpose
DX_Dynamic_Player_GFX_Enable 1 byte Zero disables player graphics loading through this system; nonzero enables it.
DX_Dynamic_Player_GFX_Addr 2 bytes Player graphics start address within the specified bank.
DX_Dynamic_Player_GFX_BNK 2 bytes reserved Player graphics source bank, specified in the low byte.
DX_Dynamic_Player_Palette_Enable 1 byte Zero disables player palette management through this system; nonzero enables it.
DX_Dynamic_Player_Palette_Addr 2 bytes Player palette source address.
DX_Dynamic_Player_Palette_BNK 1 byte Palette bank. When zero, the routine uses the game's player palette address.
DX_Dynamic_Player_CustomPlayer / DX_Dynamic_Player_LastCustomPlayer 1 byte each Reserved to identify current and previous custom players. Initialized to 0 and $FF; currently do not select character graphics by themselves.
DX_Dynamic_Yoshi_VRAMEnable 1 byte Zero disables Yoshi graphics loading through this system; nonzero enables it.
DX_Dynamic_Yoshi_Addr 2 bytes Yoshi graphics start address within the bank.
DX_Dynamic_Yoshi_BNK 1 byte Yoshi graphics source bank.

Drawing and sprite variables

DX_Drawing_InitialIndexHighPriority and DX_Drawing_InitialIndexLowPriority each occupy 1 byte and track available drawing positions for high- and low-priority sprites. DX_Drawing_OAMMap occupies 128 bytes and helps organize drawn tiles. These variables are used on LoROM; SA-1 uses its corresponding drawing management.

Individual sprite variables are defined in DynamicXSpritesTables.asm. NormalSpriteDefines.asm, ClusterSpriteDefines.asm, and ExtendedSpriteDefines.asm let all three types use the same names:

Sprite name Contents
!PoseIndex,x Desired pose.
!LastPoseIndex,x Last obtained pose available for drawing. Initialize to $FF.
!LastPoseHashIndex,x Cache entry tracking the graphics' location. Initialize to $FF.
!GlobalFlip,x / !LocalFlip,x / !LastFlip,x Overall flip, additional pose flip, and effective drawing flip.
!Palette,x Assigned palette: $00 for 8, $02 for 9, through $0E for F. Initialize to $FF for dynamic assignment.
!PaletteOption,x EPPP---- options: global effect and palette or palette-group selection.
!PaletteAssignmentOption,x Assignment mode: automatic, manual, or manual without uploading colors.
!Version,x / !LastVersion,x Sprite variant and last variant processed for its palette. Initialize !LastVersion to $FF.
!RenderXDistanceOutOfScreen,x / !RenderYDistanceOutOfScreen,x Distances that allow drawing when the sprite's position is offscreen but some of its pose may remain visible.
!SafeFrameLowByte,x / !SafeFrameHighByte,x Last frame a pose request was attempted, preventing repeated work in the same frame.
!AnimationIndex,x / !AnimationPoseIndex,x / !AnimationTimer,x Available variables for the animation, current step, and timer. The resource's code decides how to use them.
!State,x Resource state or behavior variable.

Each variable occupies 1 byte per sprite. Normal sprites have !MaxSprites entries, cluster sprites 20, and extended sprites 10. Do not use !MaxSprites as the cluster or extended entry count.

DX_FreeRams and DX_FreeRams2 mark the start of these table areas. They are not free memory for storing your own data over sprite variables.

Fixed defines

These names represent known configurations and addresses, not variables that change during gameplay.

LoROM and SA-1 compatibility

DXDefines.asm detects the ROM type and selects the corresponding values. Use these names to support both types.

Define LoROM SA-1 Use
!sa1 0 1 Select different code by ROM type.
!dp $0000 $3000 Direct-page address adjustment.
!addr $0000 $6000 !addr.
!rom $800000 $000000 Adjustment for original game code addresses.
!MaxSprites $0C (12) $16 (22) Normal sprite slot count.
!MultiplicationResult $4216 $2306 Multiplication result address.
!DivisionResult $4214 $2306 Division quotient address.
!RemainderResult $4216 $2308 Division remainder address.

Other variable-placement defines are !normalBnk, !PalCopyBNK, !Variables, and !Variables2. Normally, use each DX_... variable's name rather than calculate its address from these values.

Graphics addresses in VRAM

These defines specify transfer destinations without numeric addresses. Each family has lines 0 through 7; replace N with the line number.

Family Line 0 Increment per line Example
!FG1LineN $0000 $0100 !FG1Line1 = $0100
!FG2LineN $0800 $0100 !FG2Line1 = $0900
!BG1LineN $1000 $0100 !BG1Line1 = $1100
!FG3LineN $1800 $0100 !FG3Line1 = $1900
!BG2LineN $2000 $0100 !BG2Line1 = $2100
!BG3LineN $2800 $0100 !BG3Line1 = $2900
!LG1LineN through !LG4LineN $4000, $4400, $4800, $4C00 $0080 !LG1Line1 = $4080
!SP1LineN through !SP4LineN $6000, $6800, $7000, $7800 $0100 !SP4Line1 = $7900

!Layer1Tilemap = $3000 and !Layer2Tilemap = $3800 are the usual Layer 1 and Layer 2 tilemap addresses. VRAM addresses here use 2-byte words, as do the transfer macros.

Space configurations for dynamic poses

Pass !DynamicSpaceConfig... defines to !DynamicPoseSpaceConfig. Each name identifies the allowed graphics areas. FirstHalf and SecondHalf identify half-areas. Names containing WithPlayer or WithDSX are intended for those configurations.

Value Define
$00 !DynamicSpaceConfigSecondHalfSSP4
$01 !DynamicSpaceConfigSP4
$02 !DynamicSpaceConfigSecondHalfSP3
$03 !DynamicSpaceConfigFirstHalfSP3
$04 !DynamicSpaceConfigSP3
$05 !DynamicSpaceConfigSecondHalfSP2
$06 !DynamicSpaceConfigFirstHalfSP2
$07 !DynamicSpaceConfigSP2
$08 !DynamicSpaceConfigSecondHalfSP1
$09 !DynamicSpaceConfigFirstHalfSP1
$0A !DynamicSpaceConfigSP1
$0B !DynamicSpaceConfigFirstHalfSP1WithPlayer
$0C !DynamicSpaceConfigSP11WithPlayer
$0D !DynamicSpaceConfigSecondHalfSP3ndSP4
$0E !DynamicSpaceConfigSP34
$0F !DynamicSpaceConfigSecondHalfSP2andSP34
$10 !DynamicSpaceConfigSP234
$11 !DynamicSpaceConfigSecondHalfSP1andSP234
$12 !DynamicSpaceConfigSP1234
$13 !DynamicSpaceConfigSecondHalfSP1andSP234WithPlayer
$14 !DynamicSpaceConfigSP1234WithPlayer
$15 !DynamicSpaceConfigSecondHalfSP3ndSP4WithDSX
$16 !DynamicSpaceConfigSP34WithDSX
$17 !DynamicSpaceConfigSecondHalfSP2andSP34WithDSX
$18 !DynamicSpaceConfigSP234WithDSX
$19 !DynamicSpaceConfigSecondHalfSP1andSP234WithDSX
$1A !DynamicSpaceConfigSP1234WithDSX
$1B !DynamicSpaceConfigSecondHalfSP1andSP234WithPlayerAndDSX
$1C !DynamicSpaceConfigSP1234WithPlayerAndDSX

Spell these names exactly as published in DXDefines.asm, including names such as SecondHalfSSP4 and SP11WithPlayer.

Other constants

Define Value or use
!AN2Buffer1 / !AN2Buffer2 Animation data addresses $7EC100 and $7EAD00.
!GFX32Buffer $7E2000, the usual GFX32 buffer address.
!PlayerPaletteColor2To5 $00B280, player palette colors 2–5.
!PlayerBasePalette $00B2C8, player base palette data.
!VRAMTransferSize / !CGRAMTransferSize Both are 64 in the current template. Reserve transfer tables; they are not the per-frame byte limit.

Installation options such as !DynamicPoses, !DrawingSystem, !GraphicsChange, !PalettesChange, !PalettesEffects, !PlayerFeatures, and !YoshiFeatures come from Options.asm. They identify installed features and are not changed at runtime.

Generated defines

Dynamic Xtreme creates these defines during resource installation and adds them to DXDefines.asm. Use their names rather than copied numeric values, because IDs, addresses, and sizes may change when resources are added or modified.

Names use PascalCase normalization: remove spaces, underscores, hyphens, and other separators and capitalize each word's first letter. For example, my_sprite, my-sprite, and my sprite become MySprite. Other existing capitalization is preserved. When unsure, copy the exact name from DXDefines.asm.

Dynamic poses and drawing poses

Family or example Meaning
!DynamicPoseIDMySprite000 ID of pose graphics declared in DynamicInfo. Used to request loading with !Upload.
!DynamicPoseMySprite000 ROM address of those graphics.
!DynamicPoseMySprite000Size Graphics data size in bytes.
!PoseIDMySprite_Frame0 DrawInfo pose ID. Used for drawing with !DXDraw or DrawStatic.
!RenderBoxXDistanceOutOfScreenMySprite Horizontal distance calculated from DrawInfo to determine when the sprite is no longer visible.
!RenderBoxYDistanceOutOfScreenMySprite Vertical distance for the same check.

Dynamic IDs choose which graphics to load; drawing IDs choose how tiles form the pose. These are different values. Do not substitute a !DynamicPoseID... for a !PoseID....

Palettes and effects

Family or example Meaning
!PaletteIDMyPalette ID of a palette declared in DynamicInfo.
!PaletteMyPalette ROM address of its colors.
!PaletteMyPaletteSize Palette size in bytes.
!MySpritePaletteTableOffset Start of that DynamicInfo's palette list. Passed to !PaletteAssignment and its variants.
!PaletteEffectIDMyEffect0 Effect ID from my_effect.paleffect. The suffix is the effect's index within the file; global IDs start at 1.
!PaletteMyTargetRGB / !PaletteMyTargetHSL ROM address of a target palette converted for an RGB/HSL transition. Generated when an effect needs that palette.
!PaletteMyTargetRGBSize / !PaletteMyTargetHSLSize Converted palette sizes in bytes.

Example names follow the same normalization: my_palette becomes MyPalette and my_effect becomes MyEffect. Defines are generated during installation and stored in DXDefines.asm.

General resources and buffers

Family or example Meaning
!ResourceIDMyResource General resource ID from DynamicInfo.
!ResourceMyResource Resource ROM address.
!ResourceMyResourceSize Resource size in bytes.
!BufferIDMyBuffer ID of a tool-inserted buffer.
!BufferMyBuffer ROM address of the inserted buffer, not temporary RAM.
!BufferMyBufferSize Buffer size in bytes.

Names depend on installed resources and use the same normalization: my_resource becomes MyResource. Check DXDefines.asm for your installation's names and values.

Generated counts and tables

These defines expose counts and tables generated during installation. Normal sprites usually need only the individual IDs in the tables above.

Define Meaning
!NumberOfDynamicPoses / !NumberOfPoses Installed dynamic pose count and drawing pose count, respectively.
!NumberOfPalettes / !NumberOfResources / !NumberOfBuffers Inserted normal palette, general resource, and buffer counts.
!NumberOfPaletteEffects Value used to size effect tables. Generation includes space for ID 0; do not interpret this as a single *.paleffect file's effect count.
!PaletteTableSize Total entries in DynamicInfo palette lists.
!PaletteIDTables Address of those lists' ID table.
!PaletteAddrTables Address of the table containing their color ROM addresses.
!PaletteEffectsTypes Table of installed effect types.
!PaletteEffectsChannels1, !PaletteEffectsChannels2, !PaletteEffectsChannels3 Tables of each effect's color values.
!PaletteEffectsRatios1, !PaletteEffectsRatios2, !PaletteEffectsRatios3 Ratio tables controlling each change's strength.
!BufferTable, !PaletteTables, !PaletteEffectsTable Table addresses published by the patch.

Routine defines

These defines represent system calls, not data variables. This table helps locate them; their use is explained in the routine and macro sections.

Define or family Use
!AllowedGameMode Checks whether the current game mode permits the corresponding operations.
!DynamicPoseSpaceConfig Selects VRAM regions available for dynamic poses.
!TakeDynamicRequest Requests a pose by ID.
!DynamicRoutine Prepares pose graphics transfers to VRAM.
!SetPropertyAndOffset Gets a dynamic pose's graphics location and properties for drawing.
!Draw / !Draw_Return Drawing routine entry and return point.
!RemapOamTile Adjusts tile numbers for the actual dynamic graphics location.
!IsValid, !XIsValid, !YIsValid Drawing checks to determine tile visibility.
!AssignPalette Selects a palette destination according to assignment mode.
!LoadPaletteOnBuffer Loads palette data into a working buffer.
!SetRGBBase / !SetHSLBase Prepares colors for RGB/HSL effects.
!RGBToHSL / !HSLToRGB Converts between color spaces.
!RGBMerge Converts separate RGB colors to SNES palette format.
!Mix... / !MixAndMerge... Changes one or more color components.
!PalTransitionRGB / !PalTransitionHSL Calculates a palette transition.
!PalTransitionAndMergeRGB / !PalTransitionAndMergeHSL Calculates a transition and produces SNES-format colors.
!DoEffect / !DoEffectAndMerge Applies an effect by ID.
!PalFunctionRGB, !PalFunctionHSL, and AndMerge variants Reserved names for color functions not implemented yet.

!Routines is the table address used to obtain these calls. You do not need to calculate offsets to use published defines.

CPU state and temporary memory

Each call requires the register widths specified in its inputs. A register can return in 8-bit mode while losing its previous value: width and contents are different things.

Unless an entry states otherwise, these rules apply throughout this article:

  • Run in native mode with decimal mode disabled. The system's arithmetic uses binary numbers.
  • Direct page D: $0000 on LoROM and $3000 in the usual SA-1 Pack context. Short addresses such as $00 are relative to that base. Calls do not prepare or change D for you.
  • Data bank DBR: must allow access to the RAM mirrors and hardware registers used by the call. The usual context uses banks with these mirrors, such as $00-$3F or $80-$BF. If an operand reads a table through a 16-bit address, DBR must also allow access to that table. A 24-bit pointer carries its own bank. DynamicRoutine has a specific DBR requirement explained in its entry.
  • Calls preserve DBR and D. Some change DBR temporarily and restore it before returning. This does not make every input bank valid.
  • Flags: only use explicitly documented output flags. Do not assume N, Z, V, or carry retain their old values. A and X/Y width changes are specified per call.
  • Temporary memory: listed scratch may be overwritten on success or failure. Save values needed afterward elsewhere. Outputs are valid only under the stated conditions.
  • Stack: calls remove their own temporary stack data. Match your own pushes and pulls at the same register width and respect the routine or macro's return type.

To preserve a register, save it before the call and restore it afterward at the same width. Check returned carry before another operation changes it; restoring P with PLP also replaces that carry.

Sprite routines

These routines check whether a sprite should be drawn, load its graphics, and manage its palettes. Include the appropriate defines file first: NormalSpriteDefines.asm, ClusterSpriteDefines.asm, or ExtendedSpriteDefines.asm. Its path is relative to the sprite ASM.

DBR, D, flag, and stack requirements are explained in CPU state and temporary memory. The table below distinguishes register widths from preserved values.

Write calls such as !RenderBox or !Upload directly, as in the examples. Do not prefix them with JSR or JSL. Unless stated otherwise, X must contain the sprite slot index.

Sprite call registers and scratch

Enter with 8-bit X and Y. A's width depends on the call:

Call A on entry Registers on return Scratch potentially overwritten
!RenderBox 8 bits. A changes and returns in 8-bit mode. X and Y retain their values and widths. No scratch; modifies the Dynamic Xtreme tables described in its entry.
!PaletteAssignment and !PaletteAssignmentNoUpload 16 bits with PalettesChange enabled; 8 bits when disabled, without using the table offset. A and Y may change. X is reloaded from !SpriteIndex when used internally. The no-upload variant returns information in Y. All three return in 8-bit mode. $00-$0F, $65-$67, and $D5, depending on the path. With effects, may also use the effect temporaries listed below.
!Upload and !UploadWithoutPoseOffset 16 bits. A and Y may change. X is restored from !SpriteIndex. All three return in 8-bit mode. $00-$0F and $47-$51 when preparing a new load.
!DXDraw and !DXDrawWithOffset 16 bits. A and Y may change. When drawing, X is restored from !SpriteIndex; if no valid pose exists, input X is retained. All three return in 8-bit mode. $00-$0F, $45-$4F, and $8A. Additional offsets also use $8B-$8C.
!ReplacePalette 8 bits. A and Y may change. X is restored from !SpriteIndex. All three return in 8-bit mode. $00-$01 and temporaries used for color loading; effect temporaries when applicable.
!ConcatenateRGBPaletteEffectToGlobal and its HSL variant 16 bits. A and Y may change. X takes a palette on entry but contains !SpriteIndex on return. All three return in 8-bit mode. Effect temporaries, $05-$06, and $F0-$F3.

PIXI maintains !SpriteIndex as the current sprite index. If you temporarily change X, calls restoring that index do not necessarily preserve the X value you supplied. After effect concatenation, X identifies the sprite again, not its palette. PIXI aliases call their shared routines and continue at the next instruction; the examples' RTS belongs to the sprite's own routine.

RenderBox

!RenderBox checks whether the sprite is on screen or close enough to its edges to keep drawing. Use it at the start of the main routine to choose between core logic only (short routine) or graphics loading, palette management, and drawing as well (long routine).

Routine used by sprite type

  • Normal sprite: %DXNormalRenderBox().
  • Cluster sprite: %DXClusterRenderBox().
  • Extended sprite: %DXExtendedRenderBox().

Inputs

  • X: sprite index.
  • !RenderXDistanceOutOfScreen,x and !RenderYDistanceOutOfScreen,x: allowed horizontal and vertical distances outside the screen.

Result

  • Carry set: the sprite is on screen or within the allowed offscreen distance; run the long routine.
  • Carry clear: the sprite is too far offscreen to draw; run the short routine.

When too far offscreen, sets !Palette, !LastVersion, !LastPoseIndex, and !LastPoseHashIndex to $FF, indicating that palette and pose must be obtained again when it returns. Also combines !GlobalFlip and !LocalFlip into !LastFlip. For normal sprites, updates horizontal and vertical offscreen flags as well.

Example

print "INIT ",pc
    LDA.b #!RenderBoxXDistanceOutOfScreenMySprite
    STA !RenderXDistanceOutOfScreen,x
    LDA.b #!RenderBoxYDistanceOutOfScreenMySprite
    STA !RenderYDistanceOutOfScreen,x
    ; Put the rest of the sprite initialization here
RTL

SpriteCode:
    !RenderBox
    BCS .LongRoutine

.ShortRoutine
    LDA #$00
    %SubOffScreen()
    ; Put your sprite's logic here
RTS

.LongRoutine
    ; Put palette management, pose loading, drawing,
    ; and your sprite's logic here
RTS

The example's %SubOffScreen() is for normal sprites and takes A from 0 to 7 to select offscreen removal settings. It uses the usual value, 0. Calling it in the short routine is recommended; it is redundant in the long routine.

!RenderBoxXDistanceOutOfScreenMySprite and !RenderBoxYDistanceOutOfScreenMySprite are generated from DrawInfo and stored in DXDefines.asm. Resource names use PascalCase: remove spaces, underscores, and hyphens and capitalize each word's first letter. For example, my_sprite becomes MySprite. With multiple DrawInfo files, use the largest horizontal and vertical distances among them.

PaletteAssignment

!PaletteAssignment selects a DynamicInfo palette using !Version,x, assigns it to one of sprite palettes 8–F, and loads its colors when needed. If already assigned, resets its protection timer to 2 so another resource cannot replace it. Call every long-routine frame; this does not mean colors are uploaded every time.

If bit E of !PaletteOption,x is set ($80), it also processes the corresponding global palette effects. Otherwise the palette is excluded from the global effect.

How the palette is selected

!Version,x selects an entry from DynamicInfo's palette list: 0 for the first, 1 for the second, and so on. For a list containing red and blue palettes, versions 0 and 1 select those colors. Sprite behavior may also depend on this variable; palette management only uses it to select colors.

!PaletteOption,x determines which sprite palette may be used and whether it receives the global effect. Format: EPPP----:

  • E: 0 excludes the palette from the global effect; 1 allows it.
  • PPP: in manual mode, selects palette 8–F. 000 selects 8, 001 selects 9, through 111 for F.
  • In automatic mode, PPP = 000 allows all eight palettes. Nonzero 0XX limits the search to 8–B; 1XX limits it to C–F.
  • The low four bits are unused by these options.

!PaletteAssignmentOption,x determines who chooses the destination and whether colors are uploaded. Format: --AA----:

  • AA = 00 ($00): automatic. Dynamic Xtreme chooses an allowed palette and loads DynamicInfo's colors.
  • AA = 01 ($10): manual. Uses the palette specified by PPP and loads DynamicInfo's colors.
  • AA = 10 ($20): manual without upload. Uses the specified palette with the level's existing colors.

For normal sprites, !PaletteOption,x is !ExtraByte1,x and !PaletteAssignmentOption,x is !ExtraProp2,x. Commonly use EPPPVVVV in Extra Byte 1 and copy its low four bits to !Version,x in INIT, selecting the version through Lunar Magic. For cluster and extended sprites, the spawning code must set these variables.

For example, manual !PaletteOption,x = $A0 uses palette A and allows the global effect. With !Version,x = 1, it loads the second palette from DynamicInfo.

Routine used by sprite type

  • Normal sprite: %DXNormalPaletteAssignment().
  • Cluster sprite: %DXClusterPaletteAssignment().
  • Extended sprite: %DXExtendedPaletteAssignment().

Inputs

  • 16-bit A: !MySpritePaletteTableOffset for the DynamicInfo containing the palettes.
  • X: sprite index.
  • !Version,x: palette index in the table.
  • !LastVersion,x: last processed version, compared with !Version,x to detect changes. Initialize to $FF.
  • !PaletteOption,x: palette selection and participation in the global effect.
  • !PaletteAssignmentOption,x: automatic, manual, or manual-without-upload assignment.

Result

  • Carry set: the sprite can be drawn with the assigned palette.
  • Carry clear: the sprite has no palette ready for drawing yet.
  • !Palette,x: assigned palette, increasing by two from $00 to $0E for palettes 8–F.
  • !LastVersion,x: updated to !Version,x when the new assignment is processed.
  • A, X, and Y return in 8-bit mode.

Example

PaletteManagement:
    REP #$20
    LDA #!MySpritePaletteTableOffset
    !PaletteAssignment
RTS

!MySpritePaletteTableOffset is generated from DynamicInfo and stored in DXDefines.asm. The resource name uses PascalCase normalization: remove separators and capitalize each word's first letter, so my_sprite becomes MySprite.

PaletteAssignmentNoUpload

!PaletteAssignmentNoUpload selects a palette like !PaletteAssignment but leaves the color upload pending. Use it to modify the palette first, for example by applying a local effect combined with the global effect. If already assigned, resets its protection timer to 2 against replacement.

Bit E of !PaletteOption,x ($80) also controls global effects. This variant prepares assignment and the base palette; a subsequent routine such as !ConcatenateRGBPaletteEffectToGlobal or !ConcatenateHSLPaletteEffectToGlobal completes the effects and upload.

!Version,x selects the DynamicInfo palette: 0 for the first, 1 for the second. !PaletteOption,x uses EPPP----: E enables the global effect, and PPP selects palette 8–F manually or a search group automatically. In automatic mode, 000 allows all, nonzero 0XX allows 8–B, and 1XX allows C–F.

!PaletteAssignmentOption,x uses --AA----: $00 selects the destination automatically, $10 uses PPP, and $20 uses existing level colors. To prepare a palette for a later local effect, use a mode that reads DynamicInfo colors: automatic or manual with upload.

Routine used by sprite type

  • Normal sprite: %DXNormalPaletteAssignmentNoUpload().
  • Cluster sprite: %DXClusterPaletteAssignmentNoUpload().
  • Extended sprite: %DXExtendedPaletteAssignmentNoUpload().

Inputs

  • 16-bit A: !MySpritePaletteTableOffset for the DynamicInfo containing the palettes.
  • X: sprite index.
  • !Version,x, !PaletteOption,x, and !PaletteAssignmentOption,x: same options as !PaletteAssignment.
  • !LastVersion,x: last processed version, compared with !Version,x to detect changes. Initialize to $FF.

Result

  • Carry set: a palette is assigned; clear if one could not be obtained.
  • !Palette,x: assigned palette.
  • !LastVersion,x: updated to !Version,x when the new assignment is processed.
  • $00-$01: ID of the selected DynamicInfo palette when using a mode that loads its colors.
  • $02-$04: base palette ROM address.
  • Y = 1: palette newly assigned and effect must be calculated; Y = 0: no recalculation needed due to a new assignment. Pass this value to the concatenation routine.
  • A, X, and Y return in 8-bit mode.

Example

    REP #$20
    LDA #!MySpritePaletteTableOffset
    !PaletteAssignmentNoUpload
    BCC .Return

    ; Prepare and concatenate the effect here.
    ; Preserve Y and the $02-$04 address for that call.
.Return
RTS

$00-$01 identifies the chosen DynamicInfo palette; !Palette,x identifies the sprite palette 8–F used for drawing. $00-$04 is prepared only when the routine reads DynamicInfo. Do not treat it as output in manual-without-upload mode, with palette changes disabled, or when the current game mode does not process the call.

!MySpritePaletteTableOffset is generated from DynamicInfo and stored in DXDefines.asm. PascalCase normalization removes separators: my_sprite becomes MySprite.

Upload

!Upload makes the graphics for the sprite's current pose available for drawing. It selects the pose by adding !PoseIndex,x to the dynamic pose ID in A. If the graphics are already in VRAM, it updates their last-use record to protect them from replacement. Otherwise, it requests an upload. Call it every frame in the long routine.

Routine used by sprite type

  • Normal sprite: %DXNormalUpload().
  • Cluster sprite: %DXClusterUpload().
  • Extended sprite: %DXExtendedUpload().

Inputs

  • 16-bit A: base dynamic pose ID.
  • X: sprite index.
  • !PoseIndex,x: index added to the base ID.
  • !GlobalFlip,x and !LocalFlip,x: flip settings combined when drawing the pose.

Result

  • Carry set: the sprite has a pose it can draw. This may be the requested pose or the previous one if the new pose could not be uploaded yet.
  • Carry clear: the sprite does not have a pose it can draw yet.
  • !LastPoseIndex,x, !LastPoseHashIndex,x, and !LastFlip,x: state of the pose available for drawing.
  • A, X, and Y return in 8-bit mode.

Example

PoseManagement:
    REP #$20
    LDA #!DynamicPoseIDMySprite000
    !Upload
RTS

!DynamicPoseIDMySprite000 is generated from the DynamicInfo and written to DXDefines.asm. Names are normalized to PascalCase: separators are removed and each word starts with a capital letter, as in my_sprite → MySprite.

UploadWithoutPoseOffset

!UploadWithoutPoseOffset requests the exact dynamic pose ID in A, without adding !PoseIndex. Like !Upload, it reuses graphics already in VRAM and can keep the previous pose available until the new one can be uploaded.

Routine used by sprite type

  • Normal sprite: %DXNormalUploadWithoutPoseOffset().
  • Cluster sprite: %DXClusterUploadWithoutPoseOffset().
  • Extended sprite: %DXExtendedUploadWithoutPoseOffset() exists, but ExtendedSpriteDefines.asm currently does not provide the !UploadWithoutPoseOffset alias.

Inputs

  • 16-bit A: exact dynamic pose ID.
  • X: sprite index.
  • !GlobalFlip,x and !LocalFlip,x: sprite flip settings.

Result

  • Carry set: the sprite has a pose it can draw. Carry clear: no pose is available yet.
  • Stores the pose available for drawing in !LastPoseIndex,x and its graphics location in !LastPoseHashIndex,x, just like !Upload.
  • A, X, and Y return in 8-bit mode.

Example

PoseManagement:
    REP #$20
    LDA #!DynamicPoseIDMySprite003
    !UploadWithoutPoseOffset
RTS

!DynamicPoseIDMySprite003 is generated from the DynamicInfo and written to DXDefines.asm. Names use PascalCase, with separators removed: my_sprite becomes MySprite.

DXDraw

!DXDraw draws a pose using the dynamic graphics available to the sprite.

Routine used by sprite type

  • Normal sprite: %DXNormalDraw().
  • Cluster sprite: %DXClusterDraw().
  • Extended sprite: %DXExtendedDraw().

Inputs

  • 16-bit A: drawing pose ID.
  • X: sprite index.
  • !PropParam ($52): additional properties in YXCC---- format.
  • !MaxTilePriorityParam ($0A): maximum tile priority; $00 highest, $01 high, $02 the sprite type's own priority, and $03 lowest.
  • !Palette,x, !LastFlip,x, and !LastPoseHashIndex,x: palette, flip settings, and location of the available graphics.

Result

Draws the pose on screen. If !LastPoseHashIndex,x is $FF, nothing is drawn. A, X, and Y return in 8-bit mode.

Example

Draw:
    LDA #$20
    STA !PropParam
    LDA #$02
    STA !MaxTilePriorityParam

    LDA !LastPoseIndex,x
    REP #$21
    AND #$00FF
    ADC #!PoseIDMySprite_Frame0
    !DXDraw
RTS

In this example, the poses must appear in the same order in the DrawInfo and DynamicInfo. !LastPoseIndex identifies the pose that can actually be drawn: if the new pose could not be uploaded yet, the previous one is drawn instead.

!PoseIDMySprite_Frame0 is generated from the DrawInfo and written to DXDefines.asm. Resource and pose names are normalized to PascalCase by removing separators, as in my_sprite → MySprite. This drawing ID is separate from the DynamicInfo's !DynamicPoseID... IDs.

DXDrawWithOffset

!DXDrawWithOffset draws the pose a few pixels to the right, left, above, or below the sprite's position. This only changes where the pose is drawn; movement and collision still use the sprite's original position.

Routine used by sprite type

  • Normal sprite: %DXNormalDrawWithOffset().
  • Cluster sprite: %DXClusterDrawWithOffset().
  • Extended sprite: %DXExtendedDrawWithOffset().

Inputs

  • The same inputs as !DXDraw.
  • !XOffsetParam ($8A): signed 8-bit horizontal offset.
  • !YOffsetParam ($8B): signed 8-bit vertical offset.

Result

Draws the pose like !DXDraw, with the specified offsets added. A positive !XOffsetParam moves the drawing to the right, and a positive !YOffsetParam moves it down. Negative values move it left or up.

Example

Draw:
    LDA #$08
    STA !XOffsetParam
    LDA #$F8
    STA !YOffsetParam
    LDA #$20
    STA !PropParam
    LDA #$02
    STA !MaxTilePriorityParam

    LDA !LastPoseIndex,x
    REP #$21
    AND #$00FF
    ADC #!PoseIDMySprite_Frame0
    !DXDrawWithOffset
RTS

The example draws the pose 8 pixels to the right and 8 pixels up. !PoseIDMySprite_Frame0 is generated from the DrawInfo and written to DXDefines.asm; names are normalized to PascalCase by removing separators, as in my_sprite → MySprite.

ReplacePalette

!ReplacePalette changes the colors of the sprite's assigned palette without changing its assignment ID. For example, it can replace the normal palette during a special state. If other sprites share that palette, their colors change too. Call it when the colors need to change; calling it every frame makes the system prepare the palette and its effects again each time.

It is commonly used after !PaletteAssignmentNoUpload: assign the palette without uploading its colors, then replace them with the colors you want. Before calling !ReplacePalette, put the address of the new colors in $02-$04.

Routine used by sprite type

  • Normal sprite: %DXNormalReplacePalette().
  • Cluster sprite: %DXClusterReplacePalette().
  • Extended sprite: %DXExtendedReplacePalette().

Inputs

  • X: sprite index.
  • A in 8-bit mode.
  • $02-$04: ROM address of the new palette in BGR555 format.
  • !Palette,x: the assigned palette.
  • !PaletteOption,x: whether the palette is affected by the global effect.

Result

  • Carry set: the palette is ready for drawing. If the new colors are not ready yet, the previous colors remain available.
  • Carry clear: the palette has no colors ready for drawing yet.

Keeps the same palette ID and requests a change to its colors. When marking the effect for recalculation, it uses $FFFF if no colors have ever been ready for drawing, or $FFFE if the previous colors can still be used while the new ones are prepared.

Example

ReplaceSpritePalette:
    ; Run when an assigned palette needs new colors.
    LDA.b #!PaletteMyAlternatePalette
    STA $02
    LDA.b #!PaletteMyAlternatePalette>>8
    STA $03
    LDA.b #!PaletteMyAlternatePalette>>16
    STA $04
    !ReplacePalette
RTS

!PaletteMyAlternatePalette is the address of a palette inserted from the resources declared in a DynamicInfo. It is written to DXDefines.asm. Its name is normalized to PascalCase: my_alternate_palette becomes MyAlternatePalette.

ConcatenateRGBPaletteEffectToGlobal

!ConcatenateRGBPaletteEffectToGlobal lets the sprite's palette use its own RGB effect while still being affected by the global effect. It applies the local effect, such as PaletteTransitionRGB, first, then applies the global effect to that result. Call it after !PaletteAssignmentNoUpload, which prepares the original palette.

The usual sequence is to assign the palette with !PaletteAssignmentNoUpload, check that carry is set, then call this routine to apply the effects and finish uploading the colors.

Routine used by sprite type

  • Normal sprite: %DXNormalConcatenateRGBPaletteEffectToGlobal().
  • Cluster sprite: %DXClusterConcatenateRGBPaletteEffectToGlobal().
  • Extended sprite: %DXExtendedConcatenateRGBPaletteEffectToGlobal().

Inputs

  • 16-bit A: local RGB effect ID.
  • 8-bit X: copy the value of !Palette,x here using LDA !Palette,x followed by TAX, as shown in the example.
  • 8-bit Y: keep the value returned by !PaletteAssignmentNoUpload. A value of 1 forces the effect to be recalculated; 0 allows the previous result to be reused if nothing has changed.
  • $02-$04: BGR555 base palette address prepared by palette assignment.
  • !PaletteOption: the E bit must be set to combine the local effect with the global effect.

Result

  • Carry set: the palette can be used for drawing. If the new effect is not ready yet, the previous result remains in use.
  • Carry clear: the palette has no colors ready for drawing yet.
  • A, X, and Y return in 8-bit mode.

Example

PaletteManagement:
    REP #$20
    LDA #!MySpritePaletteTableOffset
    !PaletteAssignmentNoUpload
    BCC .Return

    LDA !Palette,x
    TAX
    REP #$20
    LDA #!PaletteEffectIDMyRGBEffect0
    !ConcatenateRGBPaletteEffectToGlobal
.Return
RTS

!MySpritePaletteTableOffset is generated from the DynamicInfo, and !PaletteEffectIDMyRGBEffect0 from a *.paleffect file. Both are written to DXDefines.asm. Names are normalized to PascalCase by removing separators, as in my_sprite → MySprite. If you use Y to select an effect from a table, save it with PHY and restore it with PLY before calling the concatenation routine.

ConcatenateHSLPaletteEffectToGlobal

!ConcatenateHSLPaletteEffectToGlobal lets the sprite's palette use its own HSL effect, such as MixH, while still being affected by the global effect. It applies the local effect first, followed by the global effect. Call it after !PaletteAssignmentNoUpload, which prepares the original palette.

The usual sequence is to assign the palette with !PaletteAssignmentNoUpload, check that carry is set, then call this routine to apply the effects and finish uploading the colors.

Routine used by sprite type

  • Normal sprite: %DXNormalConcatenateHSLPaletteEffectToGlobal().
  • Cluster sprite: %DXClusterConcatenateHSLPaletteEffectToGlobal().
  • Extended sprite: %DXExtendedConcatenateHSLPaletteEffectToGlobal().

Inputs

  • 16-bit A: local HSL effect ID.
  • 8-bit X: copy the value of !Palette,x here using LDA !Palette,x followed by TAX, as shown in the example.
  • 8-bit Y: keep the value returned by !PaletteAssignmentNoUpload. A value of 1 forces the effect to be recalculated; 0 allows the previous result to be reused if nothing has changed.
  • $02-$04: BGR555 base palette address prepared by palette assignment.
  • !PaletteOption: the E bit must be set to combine the local effect with the global effect.

Result

  • Carry set: the palette can be used for drawing. If the new effect is not ready yet, the previous result remains in use.
  • Carry clear: the palette has no colors ready for drawing yet.
  • A, X, and Y return in 8-bit mode.

Example

PaletteManagement:
    REP #$20
    LDA #!MySpritePaletteTableOffset
    !PaletteAssignmentNoUpload
    BCC .Return

    LDA !Palette,x
    TAX
    REP #$20
    LDA #!PaletteEffectIDMyHSLEffect0
    !ConcatenateHSLPaletteEffectToGlobal
.Return
RTS

!MySpritePaletteTableOffset is generated from the DynamicInfo, and !PaletteEffectIDMyHSLEffect0 from a *.paleffect file. Both are written to DXDefines.asm. Names are normalized to PascalCase: separators are removed and each word starts with a capital letter. Save and restore Y with PHY/PLY if you temporarily use it to select an effect.

Both concatenation variants handle the required conversions when the local and global effects use different color spaces. See Effect types to find out which effects use RGB or HSL.

General Routines and Macros

These routines and macros let sprites, UberASM, and other code use Dynamic Xtreme's systems. Macro arguments specify the variables and values to use. If an argument contains a comma, such as !NormalPoseIndex,x, put it in quotation marks so Asar reads it as a single argument. Use # for immediate values, such as #$04.

The rules for DBR, direct page, flags, and the stack also apply here. Each family's calling requirements explain which registers and temporary memory you must save if you need to preserve them.

Calls and returns at a glance

Some macros include the return instruction that ends the routine containing them. In those cases, code written after the macro does not run as a continuation of that routine.

Call or family Included return Usage
Sprite aliases: !RenderBox, !Upload, !DXDraw, !PaletteAssignment, and the other aliases in this category. The call returns and execution continues after the alias. Write the alias directly, without JSR or JSL. Your routine still needs its own return instruction.
GeneralUpload, Upload (general macro), and StandardSpriteUpload. RTS. Place the macro at the end of a routine called with JSR.
Draw, StandardSpriteDraw, and DrawStatic. RTS. Place the macro at the end of a routine called with JSR.
PaletteAssignment, PaletteAssignmentNoUpload, StandardSpritePaletteAssignment, and StandardSpritePaletteAssignmentNoUpload. RTL. Place the macro at the end of a routine called with JSL.
The general macro versions of ReplacePalette and ConcatenateRGBPaletteEffectToGlobal and ConcatenateHSLPaletteEffectToGlobal. RTL. Place the macro at the end of a routine called with JSL.
LoadPaletteIDAndAddress and DefineDynamicSpaceConfig. None: execution continues after the macro. You can write more code after the macro. Your routine needs its own return instruction.
TransferPaletteFromAddress, FromPointer, FromTable, Resource, and ResourceWithLength. None: execution continues after the macro. Check carry after the macro, then continue or return according to the result.
Base preparation, mixing, transition, DoEffect, and conversion macros, including their Default variants. None: execution continues after the macro. You can chain more operations after the macro. The two concatenation macros are the exceptions listed above.
VRAM and CGRAM transfer macros, including Forced variants, and math macros. None: execution continues after the macro. Your routine needs its own return instruction.
Patch routines DynamicPoseSpaceConfig, TakeDynamicRequest, and DynamicRoutine. The called routine returns with RTL. Use JSL !NombreDeLaRutina. On return, execution resumes at the instruction after JSL; your routine still needs its own return instruction.

For example, a routine containing %Upload(...) is called with JSR PoseManagement and ends with the macro's own RTS. A routine containing %PaletteAssignment(...) is called with JSL PaletteManagement and ends with its RTL. The !Upload alias works differently: it calls a shared PIXI routine and allows execution to continue afterward, rather than expanding %Upload(...) directly.

Dynamic poses

DynamicPoseSpaceConfig

Registers and temporary memory: enter with A, X, and Y in 8-bit mode. Modifies A and X; preserves Y and register sizes. Uses no scratch memory. Saves and restores DBR around configuration selection. Does not return a result in carry. Called with JSL; returns with RTL.

JSL !DynamicPoseSpaceConfig selects the parts of graphics memory (VRAM) that Dynamic Xtreme can use for dynamic poses. It is normally called from UberASM's init when entering a level.

The available configurations are defined in DXDefines.asm under names beginning with !DynamicSpaceConfig....

Input

  • 8-bit A: one of the !DynamicSpaceConfig... configurations in DXDefines.asm. Values range from $00 to $1C.

Result

After this call, Dynamic Xtreme can only place new poses in the selected regions. Changing the configuration does not remove poses already in VRAM. It is normally set when entering the level, before sprites request their graphics.

Example

init:
    LDA #!DynamicSpaceConfigSP4
    JSL !DynamicPoseSpaceConfig
RTL

!DynamicSpaceConfigSP4 is a constant in DXDefines.asm that enables SP4. Other options include !DynamicSpaceConfigSP3, !DynamicSpaceConfigSP34, and !DynamicSpaceConfigSP234. Choose a region that does not overlap the level's static graphics.

DefineDynamicSpaceConfig

%DefineDynamicSpaceConfig() specifies which VRAM regions can hold dynamic poses and which are restricted. Use it to create your own configuration instead of selecting a preset with !DynamicPoseSpaceConfig.

Parameters

%DefineDynamicSpaceConfig(init, end, restricted)
  • init: ID of the region's first 16×16 block.
  • end: ID of the region's last 16×16 block. Both endpoints are included.
  • restricted: $00 allows dynamic poses in this region; $80 restricts it, preventing the system from placing poses there.

All three parameters are assembly-time constants and are written without #. The first block must be less than or equal to the last, and both must be between $00 and $7F. For example, $60 through $7F covers 32 blocks.

Block numbering

For this configuration, sprite graphics memory is treated as a 128×256-pixel grid. The upper 128×128 half corresponds to SP1 and SP2; the lower 128×128 half corresponds to SP3 and SP4.

The grid is divided into 16×16 blocks: 8 blocks per row and 16 rows, for a total of 128 blocks. IDs run from left to right, continuing on the next row from top to bottom:

Row of 16×16 tiles Col. 0 Col. 1 Col. 2 Col. 3 Col. 4 Col. 5 Col. 6 Col. 7
0 $00 $01 $02 $03 $04 $05 $06 $07
1 $08 $09 $0A $0B $0C $0D $0E $0F
2 $10 $11 $12 $13 $14 $15 $16 $17
3 $18 $19 $1A $1B $1C $1D $1E $1F
4 $20 $21 $22 $23 $24 $25 $26 $27
5 $28 $29 $2A $2B $2C $2D $2E $2F
6 $30 $31 $32 $33 $34 $35 $36 $37
7 $38 $39 $3A $3B $3C $3D $3E $3F
8 $40 $41 $42 $43 $44 $45 $46 $47
9 $48 $49 $4A $4B $4C $4D $4E $4F
10 $50 $51 $52 $53 $54 $55 $56 $57
11 $58 $59 $5A $5B $5C $5D $5E $5F
12 $60 $61 $62 $63 $64 $65 $66 $67
13 $68 $69 $6A $6B $6C $6D $6E $6F
14 $70 $71 $72 $73 $74 $75 $76 $77
15 $78 $79 $7A $7B $7C $7D $7E $7F

Each cell represents a 16×16 tile and contains its block ID. Rows are numbered 0–15 and columns 0–7. These IDs are separate from the 8×8 TileCodes used in DrawInfo.

Section Blocks Position in the grid
SP1 $00–$1F First four rows.
SP2 $20–$3F Next four rows.
SP3 $40–$5F Next four rows.
SP4 $60–$7F Last four rows.

Examples from the patch

These configurations use the same ranges as the patch's presets. Place them in UberASM's init, before sprites request their poses.

Use SP4 only:

init:
    ; Restrict SP1, SP2, and SP3.
    %DefineDynamicSpaceConfig($00, $5F, $80)

    ; Make SP4 available for dynamic poses.
    %DefineDynamicSpaceConfig($60, $7F, $00)
RTL

Use SP3 only:

init:
    ; Restrict SP1 and SP2.
    %DefineDynamicSpaceConfig($00, $3F, $80)

    ; Make SP3 available for dynamic poses.
    %DefineDynamicSpaceConfig($40, $5F, $00)

    ; Restrict SP4.
    %DefineDynamicSpaceConfig($60, $7F, $80)
RTL

Use SP3 and SP4:

init:
    ; Restrict SP1 and SP2.
    %DefineDynamicSpaceConfig($00, $3F, $80)

    ; Make SP3 and SP4 available as a single region.
    %DefineDynamicSpaceConfig($40, $7F, $00)
RTL

Each example is an alternative configuration. Use one complete configuration covering blocks $00–$7F with consecutive, non-overlapping regions. Restrict any blocks used by the player, static sprites, or other resources whose graphics must stay in place. The macro does not detect those uses for you.

Registers and result

  • A must be in 8-bit mode. The macro modifies A and the N/Z flags; it preserves X, Y, carry, DBR, and D, and does not change register sizes.
  • Writes the configuration to DX_Dynamic_Tile_Size and DX_Dynamic_Tile_Offset at both ends of the specified region. Uses no scratch memory.
  • Does not upload graphics or move poses already in VRAM. Use it to configure the available space when entering the level.
  • Does not report success or failure in carry, and includes neither RTS nor RTL: execution continues after each call. The RTL in the examples ends UberASM's init.

TakeDynamicRequest

Registers and temporary memory: enter with 8-bit A and 16-bit X/Y. A, X, and Y return in 8-bit mode; their values may change. X is a valid cache entry index only when carry is set; it does not preserve the sprite index. May overwrite $00-$0F and $47-$51 when preparing a new upload. Preserves DBR and D on both success and failure. Called with JSL; returns with RTL.

JSL !TakeDynamicRequest takes a pose ID and looks for its graphics in VRAM. If they are already loaded, it marks them as in use to protect them from replacement. Otherwise, it finds space and schedules an upload for the next NMI, when the game updates graphics between frames.

Input

  • 16-bit Y: exact dynamic pose ID.

Result

  • Carry set: the graphics are already in VRAM or their upload has been scheduled for the next NMI. X contains the cache entry index that records their location.
  • Carry clear: there is not enough VRAM space, or uploading the pose would exceed the frame's transfer limit.
  • A, X, and Y return in 8-bit mode.

Example

RequestPose:
    REP #$10
    LDY #!DynamicPoseIDMySprite000
    JSL !TakeDynamicRequest
    BCC .Unavailable

    ; X contains the requested pose's hash table index.
    ; Your code can save this index to draw the pose.
RTS
.Unavailable
    ; Handle unavailable resources here.
RTS

!DynamicPoseIDMySprite000 is generated from the DynamicInfo and written to DXDefines.asm. Names are normalized to PascalCase by removing separators and capitalizing each word: my_sprite becomes MySprite.

This call returns the cache entry that records the graphics location, but does not store it in the sprite's variables or fall back to the previous pose if the request fails. Use !Upload for that complete behavior.

DynamicRoutine

Registers and temporary memory: sets A, X, and Y to 16-bit mode for its calculations and leaves them in 8-bit mode on return. All three values may change. May overwrite $00-$0B and $47-$51; PoseIDBackup occupies $02-$03 in direct page. Preserves D and does not change DBR: set the tables' data bank before calling it. Called with JSL; returns with RTL.

JSL !DynamicRoutine queues an upload of a pose's graphics to a specific VRAM location. The transfer takes place during NMI.

The routine needs to know which pose to copy and where to place it. It receives the destination in A, but reads the pose ID from an internal variable called PoseIDBackup. !TakeDynamicRequest prepares these values before calling it.

Required inputs

  • Low 8 bits of A: starting block in the dynamic system's VRAM map. The routine converts this value into a VRAM address.
  • PoseIDBackup: internal 16-bit variable containing the ID of the pose to copy.
  • DBR (Data Bank Register): must select the bank containing the pose address and size tables.

Before calling this routine, your code must have allocated enough VRAM space and checked that the upload fits within the frame's byte limit. !TakeDynamicRequest handles both tasks.

Result

Reads the address and amount of graphics data for the pose, calculates how to place it at the destination, and adds the required transfers to the VRAM queue. A, X, and Y return in 8-bit mode. Carry does not report success or failure.

How it works

When a sprite requests a pose that is not yet in VRAM, !TakeDynamicRequest finds a place for it. It then stores the ID in PoseIDBackup and calls !DynamicRoutine with the selected position in A. !DynamicRoutine prepares the graphics transfer; NMI performs it.

Use !Upload to load a pose from sprite code. Use !TakeDynamicRequest to request one directly by ID. Calling !DynamicRoutine also requires you to prepare the internal pose ID, data bank, and destination space described above.

GeneralUpload

Registers and temporary memory: enter with 16-bit A and 8-bit X/Y. A, X, and Y return in 8-bit mode. A and Y may change; with sprite = 1 or 2, X is restored from !SpriteIndex; with sprite = 0, X is not preserved. May overwrite $00-$0F and $47-$51, including temporary values used for a new request. Preserves DBR and D. Includes RTS: use JSR to call the routine containing the macro.


%GeneralUpload() handles pose uploads like the sprite routines, but lets you choose the variables used to store the pose index, flip settings, and graphics location. This is useful for an entity with several independent poses, or for code that uses its own tables.

Parameters

%GeneralUpload(Pose16, checkLast, sprite, flip, safeCheck, withoutOffset,
    safeLow, safeHigh, poseIndex, lastPoseIndex, lastPoseHashIndex,
    globalFlip, localFlip, lastFlip)

The signature above shows the parameter order; write the actual call on one line.

  • Pose16: 0 uses 8-bit pose indices; 1 uses 16-bit indices.
  • checkLast: 1 keeps the previous pose available if the new one cannot be obtained; 0 disables this fallback.
  • sprite: 0 uses your own variables without restoring the sprite index; 1 uses sprite tables and restores X from !SpriteIndex; 2 works like 1, but requests the pose without comparing or updating lastPoseIndex.
  • flip: 1 combines globalFlip and localFlip into lastFlip; 0 skips this update.
  • safeCheck: 1 uses safeLow and safeHigh to prevent duplicate requests in the same frame; 0 skips this check.
  • withoutOffset: 0 adds poseIndex to the base ID; 1 uses the supplied ID directly.
  • safeLow and safeHigh: variables holding the low and high bytes of the request's frame number. They are written even when safeCheck is 0.
  • poseIndex: variable holding the desired pose index.
  • lastPoseIndex: variable holding the last available pose index.
  • lastPoseHashIndex: variable identifying the entry where Dynamic Xtreme records the graphics location in VRAM. $FF means no graphics are available yet.
  • globalFlip, localFlip, and lastFlip: global, local, and combined flip variables.

Additional input

  • 16-bit A: base or exact dynamic pose ID, depending on withoutOffset.
  • With sprite = 1 or sprite = 2, !SpriteIndex must contain the sprite index.

With sprite = 2, the macro requests the pose even when poseIndex equals lastPoseIndex, and leaves lastPoseIndex unchanged. If it obtains the graphics, it stores their location in lastPoseHashIndex. If the request fails and checkLast is 1, the previous graphics can still be used. safeCheck still controls whether another request is allowed in the same frame.

The value 2 does not change the index size. Select Pose16 = 1 to use 16-bit index variables. The dynamic pose ID supplied in A is always 16-bit.

sprite = 2 is commonly used for dynamic sprites with several parts, such as a head and body whose graphics must change together. By leaving lastPoseIndex unchanged, it lets the sprite code coordinate all parts before updating the pose index. The macro does not synchronize the parts itself; the calling code handles that.

Result

Returns carry set when a pose is available for drawing: either the requested pose or the previous one if checkLast allows it. Returns carry clear if neither is available. Stores the pose's state in the supplied variables and marks its graphics as in use to protect them from replacement. The macro ends with RTS; place it at the end of a routine called with JSR.

Example

PoseManagement:
    REP #$20
    LDA #!DynamicPoseIDMySprite000
    %GeneralUpload(0, 1, 1, 1, 1, 0, "!NormalSafeFrameLowByte,x", "!NormalSafeFrameHighByte,x", "!NormalPoseIndex,x", "!NormalLastPoseIndex,x", "!NormalLastPoseHashIndex,x", "!NormalGlobalFlip,x", "!NormalLocalFlip,x", "!NormalLastFlip,x")

The example uses normal sprite tables and an 8-bit pose index. !DynamicPoseIDMySprite000 is generated from the DynamicInfo and written to DXDefines.asm; names are normalized to PascalCase by removing separators, as in my_sprite → MySprite.

Upload (general macro)

Registers and temporary memory: follows the same requirements as GeneralUpload, with Pose16 = 0. Enter with 16-bit A and 8-bit X/Y; all three return in 8-bit mode. The sprite parameter determines whether X is reloaded from !SpriteIndex. The macro includes RTS.

%Upload() is the 8-bit pose index variant of %GeneralUpload(). It is separate from the sprite alias !Upload.

Parameters

%Upload(sprite, checkLast, flip, safeCheck, withoutOffset,
    safeLow, safeHigh, poseIndex, lastPoseIndex, lastPoseHashIndex,
    globalFlip, localFlip, lastFlip)

Takes the same inputs as %GeneralUpload(), but indices stored in variables are always 8-bit. The macro includes the RTS that ends the routine.

The sprite parameter also accepts 2: this skips the comparison with lastPoseIndex and leaves that variable unchanged, but still updates lastPoseHashIndex when the pose is obtained. It does not enable 16-bit indices; use %GeneralUpload() with Pose16 = 1 for those.

This mode is commonly used to make several parts of a dynamic sprite change their graphics together. For example, your code can handle head and body uploads separately, then update the pose index when both parts should change. Leaving lastPoseIndex unchanged lets the sprite code control that change.

Example

PoseManagement:
    REP #$20
    LDA #!DynamicPoseIDMySprite000
    %Upload(1, 1, 1, 1, 0, "!NormalSafeFrameLowByte,x", "!NormalSafeFrameHighByte,x", "!NormalPoseIndex,x", "!NormalLastPoseIndex,x", "!NormalLastPoseHashIndex,x", "!NormalGlobalFlip,x", "!NormalLocalFlip,x", "!NormalLastFlip,x")

!DynamicPoseIDMySprite000 is generated from the DynamicInfo and written to DXDefines.asm. Names use PascalCase, with separators removed: my_sprite becomes MySprite.

StandardSpriteUpload

Registers and temporary memory: follows GeneralUpload with sprite = 1. Enter with 16-bit A and 8-bit X/Y. On return, X contains !SpriteIndex; A and Y may change, and all three are in 8-bit mode. Uses the same scratch memory and ends with RTS.

%StandardSpriteUpload(withoutOffset, type) automatically selects the Dynamic Xtreme tables for a normal, cluster, or extended sprite. PIXI's upload routines use this macro.

Parameters and inputs

  • withoutOffset: 0 adds !PoseIndex to the base ID; 1 uses the exact ID.
  • type: "Normal", "Cluster", or "Extended".
  • 16-bit A: dynamic pose ID.
  • !SpriteIndex: sprite index.
  • Dynamic Xtreme pose and flip tables for the selected sprite type, initialized as described for !Upload.

Result

Selects the variables for the specified sprite type. Keeps the previous pose available if the new one is not ready, updates flip settings, and prevents duplicate requests in the same frame. Carry works as it does for !Upload. The macro includes the RTS that ends the routine.

Example

PoseManagement:
    REP #$20
    LDA #!DynamicPoseIDMySprite000
    %StandardSpriteUpload(0, "Normal")

!DynamicPoseIDMySprite000 is generated from the DynamicInfo and written to DXDefines.asm. Names are normalized to PascalCase: my_sprite becomes MySprite, with separators removed.

Pose drawing

Macros in this category end the routine containing them with RTS. Call the example routines with JSR. A must hold the 16-bit drawing pose ID; prepare !PropParam ($52) and !MaxTilePriorityParam ($0A) as described for !DXDraw.

Draw

Registers and temporary memory: enter with 16-bit A and 8-bit X/Y. A and Y may change. With sprite = 1, X is restored from !SpriteIndex when preparing a valid pose; with sprite = 0, the original X is not preserved. If no valid pose is available, X is unchanged. All three return in 8-bit mode. May overwrite $00-$0F, $45-$4F, and $8A; with offset = 1, also $8B-$8C. Preserves DBR and D. Includes RTS.

%Draw() draws a dynamic pose. Its arguments specify the graphics, palette, flip settings, and position to use.

Parameters

%Draw(offset, sprite, lastPoseHashIndex, lastFlip, palette, xl, xh, yl, yh)
  • offset: 0 uses the supplied coordinates; 1 adds the signed 8-bit offsets !XOffsetParam ($8A) and !YOffsetParam ($8B).
  • sprite: 1 restores the sprite index in X from !SpriteIndex before reading its palette and position; 0 skips this step.
  • lastPoseHashIndex: variable identifying the entry where Dynamic Xtreme records the pose's graphics. If it contains $FF, nothing is drawn.
  • lastFlip: variable or value specifying the flip settings for drawing.
  • palette: palette variable or value; $00 selects palette 8, $02 selects 9, and so on up to $0E for F.
  • xl and xh: variables or values for the low and high bytes of the X position.
  • yl and yh: variables or values for the low and high bytes of the Y position.

Result

Draws the pose using the graphics identified by lastPoseHashIndex at the supplied X and Y position. You can pass a sprite's position variables or specify another position to draw from UberASM or other code.

Example

DrawPose:
    LDA #$20
    STA !PropParam
    LDA #$02
    STA !MaxTilePriorityParam
    REP #$20
    LDA #!PoseIDMySprite_Frame0
    %Draw(0, 1, "!NormalLastPoseHashIndex,x", "!NormalLastFlip,x", "!NormalPalette,x", "!SpriteXLow,x", "!SpriteXHigh,x", "!SpriteYLow,x", "!SpriteYHigh,x")

!PoseIDMySprite_Frame0 is generated from the DrawInfo and written to DXDefines.asm. Names are normalized to PascalCase by removing separators, as in my_sprite → MySprite. The ID must match the available dynamic graphics.

StandardSpriteDraw

Registers and temporary memory: follows Draw and uses the same scratch memory, with sprite = 1. Enter with 16-bit A and 8-bit X/Y. Set X to the sprite index before calling it, because it reads the hash index before restoring !SpriteIndex. All three return in 8-bit mode. Includes RTS.

%StandardSpriteDraw(offset, type) draws a dynamic pose using the Dynamic Xtreme tables for the selected sprite type.

Parameters and inputs

  • offset: 0 adds no offset; 1 uses the offsets in $8A and $8B.
  • type: "Normal", "Cluster", or "Extended".
  • 16-bit A: drawing pose ID.
  • !SpriteIndex: sprite index.
  • !PropParam and !MaxTilePriorityParam: properties and priority.
  • Dynamic Xtreme palette, last flip, and hash index tables for the selected sprite type.

Result

The same result as %Draw(), using the standard tables. PIXI's !DXDraw and !DXDrawWithOffset routines use this macro.

For normal sprites, the PIXI routines prepare the coordinate aliases !NormalXLow, !NormalXHigh, !NormalYLow, and !NormalYHigh. If you expand the macro directly, these must also be defined, as in this example.

Example

!NormalXLow = !SpriteXLow
!NormalXHigh = !SpriteXHigh
!NormalYLow = !SpriteYLow
!NormalYHigh = !SpriteYHigh

DrawPose:
    LDA #$20
    STA !PropParam
    LDA #$02
    STA !MaxTilePriorityParam
    REP #$20
    LDA #!PoseIDMySprite_Frame0
    %StandardSpriteDraw(0, "Normal")

!PoseIDMySprite_Frame0 is generated from the DrawInfo and written to DXDefines.asm. Its name uses PascalCase, with separators removed: my_sprite becomes MySprite.

DrawStatic

Registers and temporary memory: enter with 16-bit A and 8-bit X/Y. A and Y may change; X is preserved, but all three return in 8-bit mode. May overwrite $00-$0F, $45-$4F, and $8A; with offset = 1, also $8B-$8C. In this macro, sprite does not restore X from !SpriteIndex. Preserves DBR and D. Includes RTS.

%DrawStatic() draws a static pose declared with .IsStatic in the DrawInfo. Its graphics must already be loaded at the VRAM locations specified by its tiles.

Parameters

%DrawStatic(offset, sprite, lastFlip, palette, xl, xh, yl, yh)
  • offset: 0 adds no offset; 1 uses the signed offsets !XOffsetParam ($8A) and !YOffsetParam ($8B).
  • sprite: retained in the signature, but unused by the current implementation.
  • lastFlip: flip variable or value; #$00 draws without flipping.
  • palette: palette variable or value; #$00 selects palette 8, #$02 selects 9, and so on up to #$0E for F.
  • xl and xh: variables or values for the low and high bytes of the X position.
  • yl and yh: variables or values for the low and high bytes of the Y position.

Additional inputs

  • 16-bit A: static drawing pose ID.
  • !PropParam ($52) and !MaxTilePriorityParam ($0A): properties and priority.
  • !Tile ($05): tile number, when all tiles in the pose use the default value of 0. If the DrawInfo specifies other values, those tile numbers are used directly.

Result

Draws the pose without looking up the dynamic graphics cache. Requires neither !LastPoseHashIndex nor a prior call to !Upload.

Example

DrawPose:
    LDA #$80
    STA !Tile
    LDA #$20
    STA !PropParam
    LDA #$02
    STA !MaxTilePriorityParam
    REP #$20
    LDA #!PoseIDMyStaticSprite_Frame0
    %DrawStatic(0, 1, #$00, #$04, "!SpriteXLow,x", "!SpriteXHigh,x", "!SpriteYLow,x", "!SpriteYHigh,x")

The example uses tile $80 if the pose is configured to read it from !Tile, with no flipping and palette A. !PoseIDMyStaticSprite_Frame0 is generated from the DrawInfo and written to DXDefines.asm. Its name uses PascalCase: separators are removed and each word starts with a capital letter, so my_static_sprite becomes MyStaticSprite.

Paletas

These macros let you choose a sprite's palette, upload its colors, and change them during gameplay.

LoadPaletteIDAndAddress

Registers and temporary memory: enter with 8-bit A; the macro sets the register sizes it needs to calculate addresses. Modifies A and X. Y is not used, but switches to 8-bit mode and loses its high byte if it was previously 16-bit. All three return in 8-bit mode. Writes $00-$04 and reads $0E-$0F. Preserves DBR and D; carry does not report success. Execution continues after the macro.

%LoadPaletteIDAndAddress() retrieves a palette's ID and the ROM address of its colors. It selects a palette from the DynamicInfo: 0 for the first, 1 for the second, and so on. It does not upload colors; it provides these values for later use.

Inputs

  • 8-bit A: palette index within the DynamicInfo.
  • $0E-$0F: store your DynamicInfo's 16-bit !MySpritePaletteTableOffset define here, as shown in the example.

Result

  • $00-$01: palette ID.
  • $02-$04: ROM address of its colors in BGR555 format.
  • A, X, and Y return in 8-bit mode.

Example

    REP #$20
    LDA #!MySpritePaletteTableOffset
    STA $0E
    SEP #$20

    LDA #$01            ; Second palette in the DynamicInfo
    %LoadPaletteIDAndAddress()
    ; $00-$01 holds its ID; $02-$04 holds its ROM address.

In PIXI, you can also use %DXLoadPaletteIDAndAddress(), with the same inputs and results. This variant calls a shared routine, then continues with the next instruction.

!MySpritePaletteTableOffset is generated from the DynamicInfo and written to DXDefines.asm. Names are normalized by removing spaces, underscores, and hyphens, then capitalizing the first letter of each word: my_sprite becomes MySprite.

PaletteAssignment (general macro)

Registers and temporary memory: enter with 8-bit X/Y and, when PalettesChange is enabled, 16-bit A. When it is disabled, enter with 8-bit A; the table offset is not used. A and Y may change; with sprite = 1, X is restored from !SpriteIndex when needed internally; with sprite = 0, X is not preserved. All three return in 8-bit mode. May overwrite $00-$0F, $D5, and effect temporary memory when applicable. Preserves DBR and D. Ends with RTL.

%PaletteAssignment() assigns and uploads a palette like !PaletteAssignment, but lets you specify the variables holding its options and state. Use it for an additional sprite palette or for code with its own variables.

If the palette is already assigned, it keeps its use timer at 2 to protect it from replacement while in use. If option bit $80 is set, it also applies the appropriate global effect.

Parameters

%PaletteAssignment(sprite, palette, paletteAssignment, paletteOption, paletteVersion, paletteLastVersion, manualIDGroup, manualID)
  • sprite: 1 uses !SpriteIndex to access sprite tables; 0 lets you use variables independent of the sprite index.
  • palette: variable receiving the assigned palette. Initialize it to $FF. Values $00, $02, ..., $0E correspond to palettes 8–F.
  • paletteAssignment: variable or value in --AA---- format. $00 lets Dynamic Xtreme choose the destination and upload the colors; $10 uploads the colors to your chosen destination; $20 uses that destination with the colors already loaded in the level.
  • paletteOption: variable or value in EPPP---- format. E = 1 enables the global effect; E = 0 excludes this palette from it. In manual mode, PPP selects palette 8 (000) through F (111). In automatic mode, 000 allows all palettes, nonzero 0XX allows 8–B, and 1XX allows C–F. These options do not use the low four bits.
  • paletteVersion: variable or value selecting a palette from the DynamicInfo list: 0 for the first, 1 for the second, and so on. Sprite routines use !Version,x for this.
  • paletteLastVersion: variable holding the last processed version. Initialize it to $FF.
  • manualIDGroup and manualID: together, these identify the palette in manual modes. The first selects the group: #$FE for normal, #$FC for cluster, and #$FD for extended sprites. The second identifies the sprite type; normal sprites use !CustomSpriteNumber,x, as shown in the example.

You can also define your own groups: for example, manualIDGroup = #$FB for the player and #$FA for another type of entity. These are examples, not predefined Dynamic Xtreme values. Choose values not already used by another group, and use manualID to distinguish resources within each group. Groups $FE, $FD, and $FC are already used by the normal, extended, and cluster sprite routines, respectively.

Additional input

  • 16-bit A: !MySpritePaletteTableOffset for the DynamicInfo containing the palettes.
  • With sprite = 1, !SpriteIndex must contain the sprite index.

Result

  • Carry set: a palette is available for drawing. Carry clear: none is available yet.
  • Stores the palette number in palette and updates paletteLastVersion when processing the version.
  • A, X, and Y return in 8-bit mode.

Example

PaletteManagement:
    REP #$20
    LDA #!MySpritePaletteTableOffset
    %PaletteAssignment(1, "!NormalPalette,x", "!NormalPaletteAssignment,x", "!NormalPaletteOption,x", "!NormalVersion,x", "!NormalLastVersion,x", #$FE, "!CustomSpriteNumber,x")

The example uses normal sprite variables. !MySpritePaletteTableOffset is generated from the DynamicInfo and written to DXDefines.asm. Names are normalized to PascalCase by removing separators: my_sprite becomes MySprite.

Call the example routine with JSL PaletteManagement. The macro includes the RTL that ends the routine, so do not place more code after it.

PaletteAssignmentNoUpload (general macro)

Registers and temporary memory: follows PaletteAssignment (general macro). In addition to carry, returns Y as described below; do not keep another value in Y if you need it afterward. Outputs in $00-$04 are valid only when a palette was read from the DynamicInfo. Ends with RTL.

%PaletteAssignmentNoUpload() selects a palette like %PaletteAssignment(), but leaves the color upload pending. This lets you apply a local effect and combine it with the global effect afterward.

Parameters and inputs

%PaletteAssignmentNoUpload(sprite, palette, paletteAssignment, paletteOption, paletteVersion, paletteLastVersion, manualIDGroup, manualID)

Takes the same parameters as %PaletteAssignment(). A receives the 16-bit table offset. Initialize palette and paletteLastVersion to $FF.

paletteVersion selects colors from the DynamicInfo list: 0 for the first palette, 1 for the second, and so on. paletteAssignment selects the mode: $00 automatic, $10 manual, and $20 manual without uploading colors. paletteOption uses EPPP----: E enables the global effect, and PPP selects palette 8–F in manual mode. In automatic mode, 000 allows all palettes, nonzero 0XX allows 8–B, and 1XX allows C–F. To apply a local effect to the DynamicInfo colors afterward, use automatic assignment or manual assignment with upload.

Result

  • Carry set: a palette is assigned. Carry clear: assignment failed.
  • palette: assigned palette number, in steps of two from $00 to $0E.
  • paletteLastVersion: last processed version.
  • $00-$01: ID of the palette selected from the DynamicInfo.
  • $02-$04: ROM address of its colors.
  • Y = 1 means the palette was just assigned and its effect must be calculated. Y = 0 means a new assignment does not require recalculation.
  • A, X, and Y return in 8-bit mode.

Use the values in $00-$04 only when the call read a palette from the DynamicInfo. If you use an existing level palette without uploading it, disable palette changes, or call from a game mode where processing is skipped, these addresses may contain values left by other code.

Example

AssignPaletteForEffect:
    REP #$20
    LDA #!MySpritePaletteTableOffset
    %PaletteAssignmentNoUpload(1, "!NormalPalette,x", "!NormalPaletteAssignment,x", "!NormalPaletteOption,x", "!NormalVersion,x", "!NormalLastVersion,x", #$FE, "!CustomSpriteNumber,x")

After calling AssignPaletteForEffect with JSL, check carry and apply the effect using the returned values. Keep Y and $02-$04 intact for the concatenation call.

The macro ends its routine with RTL. Put the effect code after the JSL AssignPaletteForEffect call, not after the macro inside that routine.

!MySpritePaletteTableOffset is generated from the DynamicInfo and written to DXDefines.asm. Its name uses PascalCase, with separators removed: my_sprite becomes MySprite.

StandardSpritePaletteAssignment

Registers and temporary memory: follows PaletteAssignment (general macro), with sprite = 1. Enter with X set to read the sprite's tables and !SpriteIndex identifying the same sprite. Restores X from that index when needed internally; A and Y may change. Includes RTL.

%StandardSpritePaletteAssignment() uses the palette variables for the specified sprite type. PIXI's palette assignment routines use this macro.

Parameters and inputs

%StandardSpritePaletteAssignment(type, manualIDGroup, manualID)
  • type: "Normal", "Cluster", or "Extended".
  • manualIDGroup: #$FE for normal, #$FC for cluster, or #$FD for extended sprites.
  • You can also choose your own group, such as #$FB for the player or #$FA for another type of entity, provided no other group in your project uses those values.
  • manualID: variable or value identifying the sprite type in manual modes.
  • 16-bit A: !MySpritePaletteTableOffset for the DynamicInfo containing the palettes.
  • !SpriteIndex: sprite index.
  • Dynamic Xtreme palette, option, version, and last-version variables for the selected type, prepared as described for !PaletteAssignment.

Result

The same result as !PaletteAssignment: stores the assigned palette in the sprite table and returns carry set when it can be used for drawing.

The macro includes RTL. Call the example routine with JSL PaletteManagement and do not place more code after the macro.

Example

PaletteManagement:
    REP #$20
    LDA #!MySpritePaletteTableOffset
    %StandardSpritePaletteAssignment("Normal", #$FE, "!CustomSpriteNumber,x")

!MySpritePaletteTableOffset is generated from the DynamicInfo and written to DXDefines.asm. Names are normalized to PascalCase: my_sprite becomes MySprite when separators are removed.

StandardSpritePaletteAssignmentNoUpload

Registers and temporary memory: follows PaletteAssignmentNoUpload (general macro), with sprite = 1. X and !SpriteIndex must identify the same sprite on entry. Keep the returned Y and $02-$04 intact if you will concatenate an effect. Includes RTL.

%StandardSpritePaletteAssignmentNoUpload() uses the selected sprite type's variables to assign a palette, leaving the color upload pending.

Parameters and inputs

%StandardSpritePaletteAssignmentNoUpload(type, manualIDGroup, manualID)

Takes the same parameters and inputs as %StandardSpritePaletteAssignment(). A must hold the 16-bit palette table offset.

Result

The same result as !PaletteAssignmentNoUpload: stores the assigned palette and returns carry, Y, the ID in $00-$01, and the address in $02-$04, under the conditions described in that entry.

The macro includes RTL. Call the example routine with JSL AssignPaletteForEffect. Put the effect code after that call.

Example

AssignPaletteForEffect:
    REP #$20
    LDA #!MySpritePaletteTableOffset
    %StandardSpritePaletteAssignmentNoUpload("Normal", #$FE, "!CustomSpriteNumber,x")

!MySpritePaletteTableOffset is generated from the DynamicInfo and written to DXDefines.asm. Its name is normalized to PascalCase by removing separators: my_sprite becomes MySprite.

ReplacePalette (general macro)

Registers and temporary memory: enter with 8-bit A, X, and Y, and the source pointer in $02-$04. A and Y may change; with sprite = 1, X is restored from !SpriteIndex; with sprite = 0, X is not preserved. All three return in 8-bit mode. May overwrite $00-$01 and effect temporary memory when applicable. Preserves DBR and D. Ends with RTL.

%ReplacePalette() replaces the colors of a palette already in use. It works like !ReplacePalette, but lets you choose which variables to read. Other sprites or code sharing that palette will also use the new colors.

It is commonly used after assignment without upload, such as %PaletteAssignmentNoUpload(). First obtain the assigned palette, then put the address of the replacement colors in $02-$04.

Parameters and inputs

%ReplacePalette(sprite, paletteOption, palette)
  • sprite: 1 retrieves the sprite index from !SpriteIndex; 0 uses variables independent of that index.
  • paletteOption: palette option variable or value. Bit $80 controls whether the global effect is applied.
  • palette: destination palette variable or value, in steps of two from $00 to $0E.
  • $02-$04: ROM address of the new BGR555 colors.
  • A in 8-bit mode.

Result

Changes the colors without changing the assignment ID. Returns carry set when colors are available for drawing, including the previous colors if the change could not be completed yet. Returns carry clear if no result is available.

Call it when the colors need to change. Calling it every frame makes the system prepare the palette and its effects again each time.

Call the example routine with JSL ReplaceSpritePalette. The macro includes the RTL that ends the routine.

Example

ReplaceSpritePalette:
    LDA.b #!PaletteMyAlternatePalette
    STA $02
    LDA.b #!PaletteMyAlternatePalette>>8
    STA $03
    LDA.b #!PaletteMyAlternatePalette>>16
    STA $04
    %ReplacePalette(1, "!NormalPaletteOption,x", "!NormalPalette,x")

!PaletteMyAlternatePalette is the address of a palette inserted from the DynamicInfo's resources and written to DXDefines.asm. Its name is normalized by removing separators and capitalizing each word: my_alternate_palette becomes MyAlternatePalette.

Uploading colors with TransferPalette

These macros upload colors to a palette you choose, without automatic assignment. Use them to change a background palette, a palette set aside for a sprite, or colors prepared in RAM.

They also update the game's palette copies and prepare the effect state, unlike the PPU transfer macros. You do not need to copy the buffers or invalidate their effect flags manually.

Common parameters

  • CGRAMOffset: first color to change, from $00 to $FF. This is an 8-bit operand: #$91 starts at color 1 of palette 9; $00 uses the number stored in that scratch byte.
  • Length: number of bytes to copy. This is an assembly-time value, without #: $001E copies 15 colors. It must be positive and even, and the upload must not extend beyond the last CGRAM color.
  • GlobalEffect: use 0 to upload without the global effect, or 1 to apply the global effect. This is a fixed option in the call, not a RAM variable.

What GlobalEffect does

Value Behavior
0 Schedules a transfer of the colors without an effect and updates the SMW and Dynamic Xtreme buffers. If effects are enabled, excludes the modified palettes from the global effect.
1 Stores the original colors so the system can apply the global effect before uploading them. Marks the modified palettes to receive the effect and clears their previous effect flags.

With GlobalEffect = 1, no unaffected version is uploaded first: the upload waits for the effect result. This also prepares a palette for global effects when the current global effect ID is still 0. If palette effects are disabled in the installation, the call uses a direct upload.

These options affect every 16-color palette touched by the upload, not just the copied colors. When uploading with the global effect enabled, supply all visible colors of each affected palette: for example, the 15 colors of a sprite palette, starting at color 1. Do not rely on a partial upload to preserve original colors you have not prepared yet.

Common inputs and results

  • Enter with 8-bit A, X, and Y. D must be $0000 in LoROM or $3000 in the usual SA-1 Pack context. DBR must allow access to SMW's palette buffer: $0703 in LoROM or its $6703 mirror in SA-1. The usual banks with RAM mirrors, such as $00-$3F and $80-$BF, allow these accesses. If you pass tables using 16-bit addresses, DBR must also allow access to them.
  • Carry set: the direct transfer was accepted, or the original colors were prepared for the global effect. In the latter case, this does not mean the result has already been uploaded to CGRAM.
  • Carry clear: the direct transfer was rejected because of the frame's data limit. You can retry later; the buffers and effect flags for this upload remain unchanged.
  • A and X may change on both success and failure. Y may change when colors are copied; do not use it to preserve another value across calls. All three return in 8-bit mode in either case. DBR and D are preserved.
  • Calls may modify $68-$6D when effects are enabled. The FromAddress, FromTable, and Resource variants, including ResourceWithLength, also use $65-$67. Do not store the source pointer or destination color in scratch memory that the call overwrites before it finishes using those values.
  • Includes neither RTS nor RTL: execution continues after the macro.

If the source is in RAM, you must keep its contents unchanged until the scheduled transfer takes place. The source data must not cross a bank boundary. For CGRAMOffset, use an immediate value or a variable that remains valid after the macro changes X and Y; avoid operands indexed by those registers.

Call these macros when the colors need to change, rather than every frame while the palette stays the same. If you use a sprite palette at a fixed destination, restrict it so automatic assignment does not use it.

TransferPaletteFromAddress

%TransferPaletteFromAddress() uploads colors from an address known at assembly time. This can be a label in your code or a define containing the resource's full address.

Parameters

%TransferPaletteFromAddress(Address, CGRAMOffset, Length, GlobalEffect)

Example

    ; Palette 9, colors 1-F, with the global effect.
    %TransferPaletteFromAddress(!PaletteMyPalette, #$91, $001E, 1)

!PaletteMyPalette is generated when installing a palette declared in a DynamicInfo's Palettes section and written to DXDefines.asm. Names use PascalCase: spaces, underscores, and hyphens are removed, and each word starts with a capital letter. For example, my_palette becomes MyPalette.

TransferPaletteFromPointer

%TransferPaletteFromPointer() uses a source address already stored in three consecutive scratch bytes. This is useful when your code selects a palette during gameplay.

Parameters

%TransferPaletteFromPointer(Pointer, CGRAMOffset, Length, GlobalEffect)
  • Pointer: address of the first scratch byte, without #. The three bytes must hold the source address in low-byte, high-byte, bank order. For example, $02 uses $02-$04.
  • Other parameters and carry work as described in Uploading colors with TransferPalette.

Example

    ; Put the palette address in $02-$04.
    REP #$20
    LDA.w #!PaletteMyPalette
    STA $02
    SEP #$20
    LDA.b #!PaletteMyPalette>>16
    STA $04

    ; Palette 9, colors 1-F, without the global effect.
    %TransferPaletteFromPointer($02, #$91, $001E, 0)

!PaletteMyPalette is generated from a DynamicInfo palette and written to DXDefines.asm. Its name is normalized to PascalCase by removing separators: my_palette becomes MyPalette.

TransferPaletteFromTable

%TransferPaletteFromTable() accepts the source's two address bytes and bank byte separately. These can come from tables, variables, or immediate values. The macro combines them in $65-$67.

Parameters

%TransferPaletteFromTable(TableAddr, TableBNK, CGRAMOffset, Length, GlobalEffect)
  • TableAddr: operand read with 16-bit A to obtain the two address bytes.
  • TableBNK: operand read with 8-bit A to obtain the bank.
  • Other parameters and carry work as described in Uploading colors with TransferPalette.

Example

    ; $02-$03 holds the address; $04 holds the bank.
    %TransferPaletteFromTable("$02", "$04", #$91, $001E, 1)

Unlike FromPointer, the address and bank operands do not need to occupy consecutive bytes. If you read them from tables in your code, the data bank must allow access to those tables.

TransferPaletteResource

%TransferPaletteResource() uploads an entire palette registered in the DynamicInfo. You do not need to specify its address or byte count.

Parameters

%TransferPaletteResource(Name, CGRAMOffset, GlobalEffect)
  • Name: normalized palette name, without the !Palette prefix or file extension. The macro uses !Palette<Name> and !Palette<Name>Size.
  • Other parameters and carry work as described in Uploading colors with TransferPalette.

Example

    ; MyPalette contains 15 colors.
    %TransferPaletteResource(MyPalette, #$91, 1)
    BCS .Accepted
    ; Direct upload rejected: retry later.
    RTL
.Accepted
    ; Continue with the rest of the code.

The example continues at .Accepted if the direct upload was accepted or the colors were prepared for the global effect. The RTL belongs to the example, not the macro.

MyPalette corresponds to !PaletteMyPalette and !PaletteMyPaletteSize, generated in DXDefines.asm from the DynamicInfo's Palettes section. Names use PascalCase, with spaces, underscores, and hyphens removed: my_palette becomes MyPalette. This call does not use !Resource... defines from the Resources section or converted RGB/HSL palettes from a *.paleffect file.

TransferPaletteResourceWithLength

%TransferPaletteResourceWithLength() uses a registered palette's address, but copies only the number of bytes you specify.

Parameters

%TransferPaletteResourceWithLength(Name, CGRAMOffset, Length, GlobalEffect)
  • Name: normalized palette name, as in %TransferPaletteResource().
  • Length: number of bytes to copy, without #. Must not exceed the resource size.
  • Other parameters and carry work as described in Uploading colors with TransferPalette.

Example

    ; Copy 4 colors to the start of palette 9, without the global effect.
    %TransferPaletteResourceWithLength(MyPalette, #$90, $0008, 0)

MyPalette uses !PaletteMyPalette, generated from a DynamicInfo palette and written to DXDefines.asm. Its name is normalized to PascalCase by removing separators: my_palette becomes MyPalette.

TransferPaletteCommon and TransferPaletteData are internal components shared by these calls. To upload palettes from your code, use the variants above, which also prepare the effect state.

Palette effects

These macros change a palette's colors: tint it, adjust its brightness, transition between palettes, or apply an effect saved in a *.paleffect file.

For complete examples, see Applying a global palette effect in the UberASM guide, which covers a fixed effect, a cycle, and a transition. Changing palettes during gameplay shows how to upload colors with TransferPalette and check whether the call was accepted.

RGB works with red, green, and blue. HSL works with hue, saturation, and lightness. Choose RGB or HSL macros according to the change you want. See Effect types for the available effects.

Which call do you need?

To… Use…
Tint a palette or change its hue, saturation, or lightness. Mix / MixAndMerge.
Transition from one palette to another. PaletteTransition.
Apply an effect already defined in a *.paleffect file. DoEffect / DoEffectAndMerge.
Give a sprite its own effect while keeping the global effect. ConcatenateRGBPaletteEffectToGlobal or its HSL variant, after assigning the palette without upload.

To calculate an effect directly, first prepare the original colors with SetRGBBase or SetHSLBase, then apply the change. The examples use Default variants, which work with Dynamic Xtreme's palette copies so you do not need to specify memory addresses.

The AndMerge variants produce colors in the format the SNES needs to display them. Calculating them does not upload them automatically: palette handling or a CGRAM transfer completes the upload. When used after PaletteAssignmentNoUpload, the concatenation routines handle both color preparation and the upload.

Set A to 8-bit mode before calling the macros in these examples.

Effect registers and temporary memory

  • Base preparation, mixing, transition, conversion, and DoEffect macros take 8-bit A, X, and Y. Their Default variants follow the same requirements. Concatenation macros take different inputs, described in their section.
  • These macros prepare their arguments and call the patch routine internally. Execution then continues at the next instruction; they do not include a return that ends your routine.
  • Treat A, X, and Y as temporary; they return in 8-bit mode. Carry does not report success or failure: the result is the colors written to the destination buffer. DBR and D are preserved under the CPU state rules.
  • Depending on the operation, they may overwrite $00-$0F, $45-$54, $6A, $8A-$8F, and $D5-$D7. These locations hold pointers, lengths, channels, and ratios; do not store values there that must survive the call.
  • Source and destination banks are read with 8-bit A; addresses, offsets, and color counts are read with 16-bit A during preparation. Channels and ratios are read with 8-bit A. The DoEffect ID is read with 16-bit A. Prepare memory operands at the corresponding size.
  • If your operands are indexed, X and Y must identify their data on entry. Their values may change during the call. In the full variants, arguments must also avoid overlapping temporary values prepared before those arguments are read.

Preparing colors: SetRGBBase and SetHSLBase

Before changing colors, you need a working copy for the effect calculations. %SetRGBBase() prepares it for RGB effects; %SetHSLBase() prepares it for HSL. If the original colors have not changed, you can reuse this copy in later frames.

The Default variants only require the first color and the number of colors to prepare:

%SetRGBBaseDefault(offset, length)
%SetHSLBaseDefault(offset, length)

offset is the first color number, and length is the color count. For example, #$0081 and #$000F select palette 8's 15 visible colors.

Use the full versions if you need to choose the addresses to read from and write to:

%SetRGBBase(src, srcbnk, dst, dstbnk, offset, length)
%SetHSLBase(src, srcbnk, dst, dstbnk, offset, length)

%SetRGBBaseDefault(offset, length) and %SetHSLBaseDefault(offset, length) read from DX_PPU_CGRAM_PaletteCopy and write the result to DX_PPU_CGRAM_BaseRGBPalette or DX_PPU_CGRAM_BaseHSLPalette.

Example: preparing a sprite palette

    ; Palette 8, excluding its first, transparent color.
    %SetRGBBaseDefault(#$0081, #$000F)

    ; Use this variant instead to work in HSL:
    ; %SetHSLBaseDefault(#$0081, #$000F)

$81 is palette 8's second color: the palette starts at color $80, and the next 15 colors are processed.

Mixing colors: Mix and MixAndMerge

These macros move the original colors toward values you specify. For example, move red toward 31 and green and blue toward 0 to tint a palette red. Changing the ratios gradually makes the effect fade in.

The v parameters specify the target color values, from 0 to 31. The r parameters control how far to move toward them: 0 keeps the original, 127 applies the full change, and 64 applies roughly half. The examples write these as #$00, #$7F, and #$40.

For RGB, components 1, 2, and 3 are red, green, and blue. For HSL, they are hue, saturation, and lightness.

The end of the name identifies the components to change:

RGB HSL Components changed
R, G, B H, S, L One.
RG, RB, GB HS, HL, SL Two.
RGB HSL All three.

Each combination is available as Mix..., MixAndMerge..., and MixAndMerge...Default. For example: %MixR(), %MixAndMergeR(), and %MixAndMergeRDefault().

Supply the selected components' ratios first, then their target values, in the order given by the name. Source and destination addresses, first color, and color count follow. Default variants omit the addresses.

%MixRGB(r1, r2, r3, v1, v2, v3, src, srcbnk, dst, dstbnk, offset, length)
%MixAndMergeRG(r1, r2, v1, v2, src, srcbnk, dst, dstbnk, offset, length)
%MixAndMergeLDefault(r3, v3, offset, length)

RGB example: tinting a palette red

    ; Prepare palette 8's original colors.
    %SetRGBBaseDefault(#$0081, #$000F)

    ; Move all three components roughly halfway toward pure red.
    %MixAndMergeRGBDefault(#$40, #$40, #$40, #$1F, #$00, #$00, #$0081, #$000F)

The first three #$40 values set the strength of the change for red, green, and blue. The next three, #$1F, #$00, #$00, specify pure red as the target. The last two select palette 8's 15 colors.

HSL example: lightening a palette

    %SetHSLBaseDefault(#$0081, #$000F)
    %MixAndMergeLDefault(#$40, #$1F, #$0081, #$000F)

Only lightness changes here: #$40 applies roughly half the change, and #$1F is maximum lightness. This example is independent of the previous one.

Transitioning between palettes: PaletteTransition

These macros move each original color toward the corresponding color in another palette. Unlike Mix, each color has its own target. For example, they can transition a sprite from its normal colors to a damage palette.

The variants are PaletteTransitionRGB, PaletteTransitionHSL, PaletteTransitionRGBAndMerge, and PaletteTransitionHSLAndMerge. Both AndMerge variants also have a Default version.

%PaletteTransitionRGB(r1, r2, r3, src, srcbnk, targetpal, targetpalbnk, dst, dstbnk, offset, length)
%PaletteTransitionHSLAndMerge(r1, r2, r3, src, srcbnk, targetpal, targetpalbnk, dst, dstbnk, offset, length)
%PaletteTransitionRGBAndMergeDefault(r1, r2, r3, targetpal, targetpalbnk, offset, length)

targetpal and targetpalbnk specify the target palette's address and bank. Prepare it with 3 bytes per color, in RGB or HSL according to the macro. The ratios control how far each component moves toward its target.

Example: calculating an intermediate transition step

    ; This example uses the palette copy's first 15 colors.
    %SetRGBBaseDefault(#$0000, #$000F)
    %PaletteTransitionRGBAndMergeDefault(#$40, #$40, #$40, #!PaletteMyTargetRGB, #!PaletteMyTargetRGB>>16, #$0000, #$000F)

The example assumes !PaletteMyTargetRGB is an RGB target palette generated when installing a transition *.paleffect. Its address is written to DXDefines.asm. Names are normalized by removing spaces, hyphens, and underscores and capitalizing each word: my_target becomes MyTarget. For HSL, use the HSL-converted palette and the corresponding macro.

Applying an effect from a file: DoEffect

%DoEffect() applies an effect from a *.paleffect file by ID. The file already defines the effect type, color values, and ratios, so you do not need to supply them individually.

%DoEffect(effect, src, srcbnk, dst, dstbnk, offset, length)
%DoEffectAndMerge(effect, src, srcbnk, dst, dstbnk, offset, length)
%DoEffectAndMergeDefaultRGB(effect, offset, length)
%DoEffectAndMergeDefaultHSL(effect, offset, length)
  • effect: effect ID, such as #!PaletteEffectIDMyEffect0.
  • The source must contain separate RGB or HSL components, depending on the selected effect.
  • DoEffect writes 3 bytes per color so you can apply further changes.
  • DoEffectAndMerge writes colors in SNES format. The DefaultRGB and DefaultHSL variants select the corresponding base and write to DX_PPU_CGRAM_PaletteWriteMirror.

Example: applying an RGB effect to palette 8

    %SetRGBBaseDefault(#$0081, #$000F)
    %DoEffectAndMergeDefaultRGB(#!PaletteEffectIDMyEffect0, #$81, #$000F)

!PaletteEffectIDMyEffect0 is generated from a *.paleffect file and written to DXDefines.asm. Its name is normalized to PascalCase by removing separators: my_effect becomes MyEffect. The final number identifies the effect within the file. For an HSL effect, prepare the HSL base and use %DoEffectAndMergeDefaultHSL().

Converting between RGB and HSL

%RGBToHSL() converts RGB colors to HSL; %HSLToRGB() converts them back. Both read and write 3 bytes per color. Use them when you want to continue modifying a palette with an effect that uses the other color space.

%RGBToHSL(src, srcbnk, dst, dstbnk, offset, length)
%HSLToRGB(src, srcbnk, dst, dstbnk, offset, length)

Example: converting the RGB base to HSL

    %RGBToHSL(#DX_PPU_CGRAM_BaseRGBPalette, #DX_PPU_CGRAM_BaseRGBPalette>>16, #DX_PPU_CGRAM_BaseHSLPalette, #DX_PPU_CGRAM_BaseHSLPalette>>16, #$0081, #$000F)

The example reads palette 8's 15 colors from the RGB base and writes their equivalents to the HSL base. Prepare the RGB base before calling the conversion.

Combining a local effect with the global effect

Registers and temporary memory: these two macros take 16-bit A, 8-bit X holding the palette value, and 8-bit Y holding the result of assignment without upload. All three return in 8-bit mode. With sprite = 1, X is restored from !SpriteIndex; with sprite = 0, X is not preserved. A and Y may change. Uses effect temporary memory and $F0-$F3. Preserves DBR and D. Unlike the other effect macros, these include RTL and report availability through carry, including when reusing a previous result.

%ConcatenateRGBPaletteEffectToGlobal() and %ConcatenateHSLPaletteEffectToGlobal() apply a local effect to a palette that must also receive the global effect. The sprite calls of the same name use these macros, but the macros let you choose your palette variables.

They are commonly used after %PaletteAssignmentNoUpload() or its sprite variant: assign the palette without uploading colors, then apply the effects. Keep the assignment's returned values intact for this second call.

%ConcatenateRGBPaletteEffectToGlobal(sprite, paletteOption, palette)
%ConcatenateHSLPaletteEffectToGlobal(sprite, paletteOption, palette)
  • sprite: 1 restores the sprite index from !SpriteIndex; 0 lets you use your own variables.
  • paletteOption: palette option variable or value; set bit $80 to include the global effect.
  • palette: variable holding the assigned palette, in steps of two from $00 to $0E.
  • 16-bit A: local RGB or HSL effect ID, according to the macro.
  • 8-bit X: copy of the assigned palette value.
  • 8-bit Y: keep the value returned by assignment without upload.
  • $02-$04: original color address prepared by that assignment.

Returns carry set if drawing can use the requested effect, or the previous effect while the new one is being prepared. Returns carry clear if no result is available yet. Both macros end the routine with RTL.

Example: completing assignment without upload

; Call this routine with JSL after assigning the palette without upload.
; Keep Y and $02-$04 from that call intact.
ApplyLocalEffect:
    LDA !NormalPalette,x
    TAX
    REP #$20
    LDA #!PaletteEffectIDMyEffect0
    %ConcatenateRGBPaletteEffectToGlobal(1, "!NormalPaletteOption,x", "!NormalPalette,x")

!PaletteEffectIDMyEffect0 is generated from a *.paleffect file and written to DXDefines.asm. Its name is normalized to PascalCase by removing separators: my_effect becomes MyEffect. Use the HSL macro for an HSL effect. If you use Y to read an effect table, save it with PHY and restore it with PLY before concatenating.

Choosing addresses and combining several effects

The examples' Default variants choose the addresses for you. Use the full versions to store results elsewhere or apply several effects in sequence. Their address parameters are:

  • src: where to read the original colors; srcbnk: their bank.
  • dst: where to write the result; dstbnk: its bank. The destination must be RAM available to your code.
  • offset: first color to process, counting from 0.
  • length: number of colors to process.

For a fixed address, use #Nombre for the address and #Nombre>>16 for its bank, as in the RGB-to-HSL example. You can also pass variables containing these values.

The required space depends on the operation:

Operation Reads Writes
SetRGBBase / SetHSLBase Standard palette: 2 bytes per color. Separate components: 3 bytes per color.
Mix, PaletteTransition, DoEffect without AndMerge Separate components: 3 bytes per color. Separate components: 3 bytes per color.
AndMerge variants Separate components: 3 bytes per color. Standard palette: 2 bytes per color.
RGBToHSL / HSLToRGB Separate components: 3 bytes per color. Separate components: 3 bytes per color.

For example, to apply two effects in sequence, the first must write separate components, without AndMerge. The second reads that result and uses AndMerge to prepare the palette for upload. If one effect uses RGB and the other HSL, convert the colors between steps.

Depending on the operation, the macros use temporary memory including $45-$50, $51-$53, $6A, $8A, $8C, $8E, $0E-$0F, and $D5-$D7. Do not store values there that you need to keep after the call.

PPU

These macros upload graphics to VRAM and colors to CGRAM. The SNES stores graphics in VRAM and their palettes in CGRAM.

A call schedules a transfer for the next NMI, when the game can update those memories. It does not copy the data immediately. The source data must remain available until the transfer takes place.

Which call to use

To… VRAM CGRAM
Supply the source address and bank separately. TransferToVRAM TransferToCGRAM
Supply a full address and let the macro extract the bank. TransferToVRAMFromAddr TransferToCGRAMFromAddr
Upload an entire resource registered in Dynamic Xtreme. TransferResourceToVRAM TransferResourceToCGRAM
Upload a specified number of bytes from a resource. TransferResourceToVRAMWithLenght TransferResourceToCGRAMWithLenght

The normal versions check whether the transfer fits within the frame's byte limit:

  • Carry set: the transfer was scheduled.
  • Carry clear: the transfer was not scheduled because it would exceed the limit. Your code can retry in another frame.

This limit is shared by all Dynamic Xtreme uploads. Several transfers in one frame use the same allowance. These macros modify A and X. If accepted, the transfer leaves A, X, and Y in 8-bit mode; if rejected, A returns in 8-bit mode, but X and Y do not necessarily change size.

Transfer limits and requirements

Keep these limits in mind before preparing the inputs:

Byte budget and transfer list capacity

Limit What it controls Default
DX_Dynamic_MaxDataPerFrame Total number of bytes that may be requested in one frame. Compared against DX_Dynamic_CurrentDataSend, which counts the bytes requested by all resources. $0800 bytes, shared by Dynamic Xtreme uploads.
VRAM list Number of requests its tables can hold. Each entry uses two bytes per table; !VRAMTransferSize allocates the byte count for each table. !VRAMTransferSize = 64: room for 32 requests.
CGRAM list Number of requests its tables can hold. !CGRAMTransferSize sets the number of available entries. !CGRAMTransferSize = 64: room for 64 requests.

These are separate limits. For example, 32 VRAM transfers of 2 bytes request only 64 bytes, but fill all 32 entries in the default list. You cannot add another request just because some byte budget remains.

Normal macros check the byte budget, but do not check for a free list entry. If the list is full, your code must postpone the request. Adding it anyway can write beyond the tables and corrupt other variables; there is no separate failure result for a full list. Include requests from other systems in your count: a dynamic pose may require several transfers.

Forced variants skip the byte budget check and add the requested amount to DX_Dynamic_CurrentDataSend. They do not increase list capacity or the time available for transfers. List counters are 8-bit and the byte counter is 16-bit: do not let them overflow.

Transfer requirements

Value Your code's responsibility
Length Use a positive, even byte count. In DMA, $0000 means 65536 bytes, not an empty transfer. PPU macros do not check these conditions.
Source The resource or buffer must contain the requested number of bytes, without crossing a bank boundary. DMA does not continue automatically into the next bank. Split the upload if needed.
RAM source You must keep the buffer unchanged until NMI performs the transfer. The list stores its address, not a copy of its data. In SA-1, use memory that the S-CPU can also read, since it performs the transfer.
VRAM destination The address is measured in 2-byte words. Both the start and end must be within $0000–$7FFF and within the graphics space available to your resource. For example, $0200 bytes starting at $6000 occupy words $6000–$60FF.
CGRAM destination The destination specifies the first color. Each color occupies 2 bytes; the upload must not extend beyond color $FF. For example, 15 colors fit from $F1 onward, or $001E bytes.

Carry set from a normal macro means the request was accepted within the byte budget. It does not confirm that the source, destination, length, or list capacity was validated. These requirements also apply to Forced variants.

Use TransferPalette to change colors while updating SMW's palette copies and the effect state. PPU transfers to CGRAM only schedule the copy.

Register state

  • Enter with 8-bit A, X, and Y. Normal versions set A to 16-bit mode internally to check the budget, but this entry state also supports Forced variants, especially CGRAM transfers.
  • On acceptance, A and X change and all three registers return in 8-bit mode. Y is not used as a working register, but loses its high byte if you entered with 16-bit indices. On rejection, A changes and returns in 8-bit mode; X and Y retain their original values and sizes.
  • Uses no scratch memory to prepare the request. Writes to the transfer lists and frame byte counter. Preserves DBR and D. DBR must allow access to the counter; some variants may also need that bank to read your memory operands.
  • Source addresses and lengths are read with 16-bit A. The VRAM destination and bank argument are read with 16-bit A; supply the bank as a 16-bit value with a zero high byte. For CGRAM, the source bank and destination color are read with 8-bit A.
  • X changes before several arguments are read. Do not pass X-indexed addresses assuming X will still hold the sprite index. Prefer immediate values or variables independent of X for transfer arguments.
  • Includes neither RTS nor RTL. Execution continues after expansion. Check carry from normal variants before another operation changes it. Forced variants do not return a success flag in carry.

These rules also apply to FromAddr, Resource, and WithLenght variants: they prepare the arguments and expand the same transfer macro.

TransferToVRAM

%TransferToVRAM() schedules a graphics transfer from ROM or RAM to VRAM.

%TransferToVRAM(VRAMOffset, ResourceAddr, ResourceBNK, Lenght)
  • VRAMOffset: VRAM destination address, measured in 2-byte words. For example, copying $0200 bytes starting at $6000 occupies words $6000-$60FF.
  • ResourceAddr: source address within its bank.
  • ResourceBNK: source bank.
  • Lenght: number of bytes to copy. This spelling is retained because it is the parameter's actual name.

Example: scheduling a graphics upload

    %TransferToVRAM(#$6000, #!ResourceMyGraphics, #!ResourceMyGraphics>>16, #$0200)
    BCC .NotLoaded
    ; The transfer will take place during the next NMI.
.NotLoaded

!ResourceMyGraphics is the address of a resource registered through a DynamicInfo and written to DXDefines.asm. Its name is normalized by removing spaces, hyphens, and underscores and capitalizing each word: my_graphics becomes MyGraphics. The resource must contain the requested number of bytes.

TransferToVRAMFromAddr

Performs the same transfer, but takes the full source address. The macro separates the address and bank for you.

%TransferToVRAMFromAddr(VRAMOffset, addr, Lenght)

Write addr without #; supply the other parameters as in TransferToVRAM.

Example

    %TransferToVRAMFromAddr(#$6000, !ResourceMyGraphics, #$0200)
    BCC .NotLoaded
.NotLoaded

!ResourceMyGraphics is generated from a DynamicInfo resource and written to DXDefines.asm. Its name is normalized to PascalCase: my_graphics becomes MyGraphics.

TransferResourceToVRAM and TransferResourceToVRAMWithLenght

These variants take the name of a Dynamic Xtreme resource. The first copies the entire resource; the second copies the number of bytes you specify.

%TransferResourceToVRAM(VRAMOffset, ResourceName)
%TransferResourceToVRAMWithLenght(VRAMOffset, ResourceName, Lenght)

For a resource with defines !ResourceMyGraphics and !ResourceMyGraphicsSize, use "MyGraphics" as ResourceName.

Example: uploading the entire resource

    %TransferResourceToVRAM(#$6000, "MyGraphics")
    BCC .NotLoaded
.NotLoaded

Example: uploading only its first $0200 bytes

    %TransferResourceToVRAMWithLenght(#$6000, "MyGraphics", #$0200)
    BCC .NotLoaded
.NotLoaded

The name and size come from the defines generated in DXDefines.asm when installing DynamicInfo resources. Use the normalized name: my_graphics becomes MyGraphics, with separators removed and each word capitalized.

TransferToCGRAM

%TransferToCGRAM() schedules a color transfer from ROM or RAM to a SNES palette.

%TransferToCGRAM(CGRAMOffset, TableAddr, TableBNK, Lenght)
  • CGRAMOffset: first destination color number, from $00 to $FF. For example, palette 8 starts at color $80.
  • TableAddr and TableBNK: address and bank of the colors to copy.
  • Lenght: byte count. Each color occupies 2 bytes; 15 colors occupy $001E bytes.

Source colors must use the standard SNES BGR555 format. If you calculated an effect, use its final 2-byte-per-color result, such as the output of an AndMerge variant.

Example: copying palette 8's 15 visible colors

    %TransferToCGRAM(#$81, #DX_PPU_CGRAM_PaletteWriteMirror+$0102, #DX_PPU_CGRAM_PaletteWriteMirror>>16, #$001E)
    BCC .NotLoaded
.NotLoaded

This example uses an effect result stored in DX_PPU_CGRAM_PaletteWriteMirror. Offset $0102 corresponds to color $81, since each color occupies 2 bytes. It skips the palette's first color, which is transparent for sprites.

TransferToCGRAMFromAddr

Performs the same color transfer, but takes the full source address. Write addr without #.

%TransferToCGRAMFromAddr(CGRAMOffset, addr, Lenght)

Example

    %TransferToCGRAMFromAddr(#$81, DX_PPU_CGRAM_PaletteWriteMirror+$0102, #$001E)
    BCC .NotLoaded
.NotLoaded

The result is the same as in the previous example: schedules an upload of 15 colors starting at palette 8's second color.

TransferResourceToCGRAM and TransferResourceToCGRAMWithLenght

These variants copy colors from a resource registered in Dynamic Xtreme. The first uses the full resource size; the second uses the byte count you specify.

%TransferResourceToCGRAM(CGRAMOffset, ResourceName)
%TransferResourceToCGRAMWithLenght(CGRAMOffset, ResourceName, Lenght)

Example: copying an entire color resource

    %TransferResourceToCGRAM(#$80, "MyColors")
    BCC .NotLoaded
.NotLoaded

Example: copying only 15 colors

    %TransferResourceToCGRAMWithLenght(#$81, "MyColors", #$001E)
    BCC .NotLoaded
.NotLoaded

These examples assume a general color resource with defines !ResourceMyColors and !ResourceMyColorsSize, generated in DXDefines.asm from a DynamicInfo. Use the normalized name: my_colors becomes MyColors. In the second example, the resource must begin with the 15 colors to copy, without a transparent color before them.

Forced variants

All transfers above have a variant whose name begins with Forced. These schedule the transfer even if it exceeds the frame's byte limit. The byte count is still added to the frame's total.

VRAM CGRAM
ForcedTransferToVRAM ForcedTransferToCGRAM
ForcedTransferToVRAMFromAddr ForcedTransferToCGRAMFromAddr
ForcedTransferResourceToVRAM ForcedTransferResourceToCGRAM
ForcedTransferResourceToVRAMWithLenght ForcedTransferResourceToCGRAMWithLenght

They take the same parameters as the versions without Forced. Carry does not report whether the transfer was accepted. Set A to 8-bit mode before using them; A, X, and Y return in 8-bit mode.

Example

    %ForcedTransferResourceToVRAM(#$6000, "MyGraphics")
    ; Do not check carry for this variant.

MyGraphics corresponds to the DynamicInfo resource !ResourceMyGraphics in DXDefines.asm. Use the normalized name, as in my_graphics → MyGraphics.

Use Forced for uploads that need priority and whose data size you already control. Forcing too much data can exceed the time available for graphics updates. For other uploads, use the normal variants and check carry.

Math

These macros multiply and divide using SNES or SA-1 hardware, depending on the ROM. They use unsigned numbers: values represent positive quantities or zero.

Set A to 8-bit mode before calling them. The macros do not change the size of A, X, or Y for you.

Registers and temporary memory: all macros in this category, including W and After... variants, modify A and N/Z, preserve X, Y, carry, DBR, and D, and use no scratch memory. They retain their entry register sizes; A must be 8-bit for correct hardware writes. DBR must allow access to the SNES or SA-1 arithmetic registers as appropriate. Includes neither RTS nor RTL; execution continues after the macro. Results are in the hardware registers listed below, not in A. Carry does not report success or arithmetic overflow.

Writing the arguments

Mul, Div, MulAfterMul, and DivAfterDiv append the argument directly to LDA. Put the argument in quotation marks with a leading space: " #$05" produces LDA #$05.

You can also start the argument with .b, .w, or .l to select the memory access encoding. For example, if $02 holds the number to multiply:

    %Mul(".b $02", " #$07")

This generates LDA.b $02 to read the first number and LDA #$07 for the second. These suffixes control address encoding; they do not change A's size. The multiplication still uses 8-bit numbers.

Variants with W, along with MulAfterDiv and DivAfterMul, already add a space before their arguments. Write their values normally, as in %MulW(#$05, #$07). The current implementation does not accept .b, .w, or .l suffixes in the same way for these variants, because the inserted space would separate LDA from the suffix.

What does W mean?

The hardware needs a short time to finish the calculation. Macros with W include that wait, so you can read the result immediately afterward. Versions without W let you run other code while the hardware calculates, but you must allow enough time before reading the result. Use a W variant if you need the result in the next instruction.

Read results through these defines:

  • !MultiplicationResult: 16-bit multiplication result.
  • !DivisionResult: 16-bit division quotient.
  • !RemainderResult: 16-bit division remainder.

These defines select the correct SNES or SA-1 address. The next calculation may overwrite previous results; save them if you need them later.

Mul and MulW

Multiply two 8-bit values to produce a 16-bit result.

%Mul(a, b)
%MulW(a, b)

a and b can be immediate values, such as #$06, or 8-bit variables.

Example: 6 × 7 = 42

    %MulW(#$06, #$07)
    REP #$20
    LDA !MultiplicationResult    ; A = $002A, or 42
    SEP #$20

The call without a wait is %Mul(" #$06", " #$07"). Do not read its result immediately: first run other code that does not use the multiplication or division hardware.

Div and DivW

Divide a 16-bit number by an 8-bit number. Supply the 16-bit number as two bytes: high byte first, then low byte.

%Div(ah, al, b)
%DivW(ah, al, b)
  • ah: high byte of the number to divide.
  • al: low byte of the same number.
  • b: divisor, from 1 to 255.

Example: 300 ÷ 7 = 42, remainder 6

    ; 300 = $012C: high byte $01, low byte $2C.
    %DivW(#$01, #$2C, #$07)

    REP #$20
    LDA !DivisionResult          ; A = $002A, or 42
    LDA !RemainderResult         ; A = $0006
    SEP #$20

You can also pass a 16-bit variable as two arguments: %DivW(MiNumero+1, MiNumero, #$07). Here, MiNumero represents a variable defined by your code.

Continuing from the previous result

AfterMul and AfterDiv variants use the previous calculation's result as the first number. You only need to supply the second number.

Macro Calculation
MulAfterMul(b) / MulWAfterMul(b) Multiplies the low 8 bits of the previous multiplication result by b.
MulAfterDiv(b) / MulWAfterDiv(b) Multiplies the low 8 bits of the previous quotient by b.
DivAfterMul(b) / DivWAfterMul(b) Divides the full previous multiplication result by b.
DivAfterDiv(b) / DivWAfterDiv(b) Divides the full previous quotient by b.

W still means the new result can be read immediately afterward. After... variants already include the wait needed to read the previous calculation's result.

Example: two multiplications, (6 × 7) × 2 = 84

    %Mul(" #$06", " #$07")
    %MulWAfterMul(#$02)
    REP #$20
    LDA !MultiplicationResult    ; A = $0054, or 84
    SEP #$20

Example: division followed by multiplication, (100 ÷ 4) × 3 = 75

    %Div(" #$00", " #$64", " #$04")
    %MulWAfterDiv(#$03)
    REP #$20
    LDA !MultiplicationResult    ; A = $004B, or 75
    SEP #$20

Example: multiplication followed by division, (20 × 15) ÷ 4 = 75

    %Mul(" #$14", " #$0F")
    %DivWAfterMul(#$04)
    REP #$20
    LDA !DivisionResult          ; A = $004B, or 75
    SEP #$20

Example: two divisions, (300 ÷ 3) ÷ 4 = 25

    %Div(" #$01", " #$2C", " #$03")
    %DivWAfterDiv(#$04)
    REP #$20
    LDA !DivisionResult          ; A = $0019, or 25
    SEP #$20

In multiplication, the first number is always limited to 8 bits. If the previous result exceeds 255, MulAfterMul or MulAfterDiv uses only its low byte. The division macros in this table use the full 16-bit previous result.