data/claims/earthbound-av.yamlEarthBound battle backgrounds (Herringway's ebsrc)
Game: EarthBound.
EarthBound's battle backgrounds are built from 327 layer entries of 17 bytes each: the layer's graphics and palette, its bit depth (2 or 4 bits a pixel), a palette-cycling style with up to two ranges of colors and the frames between steps, up to four scrolling movements, and up to four distortions, each played in turn. 275 entries cycle colors, 237 scroll, and 285 distort. earthbound-av/layers
src/data/battle/backgrounds/config_table.asm#L1-L5887
BG_DATA_TABLE:
.BYTE $00 ;Graphics
.BYTE $00 ;Palette
.BYTE $02 ;BPP
.BYTE $03 ;???
.BYTE $01 ;Palette cycle #1 - first palette
.BYTE $03 ;Palette cycle #1 - last palette
.BYTE $00 ;Palette cycle #2 - first palette
.BYTE $00 ;Palette cycle #2 - last palette
.BYTE $1E ;Palette change speed
.BYTE $00 ;Scrolling movement #1
…ebsrc: Source code recreation for the game Earthbound · include/structs.asm#L447-L465.STRUCT bg_layer_config_entry
graphics .byte ;0
palette .byte ;1
bitdepth .byte ;2
palette_shifting_style .byte ;3
palette_cycle_1_first .byte ;4
palette_cycle_1_last .byte ;5
palette_cycle_2_first .byte ;6
palette_cycle_2_last .byte ;7
palette_change_speed .byte ;8
scrolling_movement_1 .byte ;9
scrolling_movement_2 .byte ;10
…EarthBound (USA) · 0xadca1-0xaf257 — $CA:DCA1-$CA:F257: BG_DATA_TABLE, 17 bytes a layerEach frame, unless the layer's freeze flag is set, a background layer's speed grows by its acceleration and its position by its speed, horizontally and vertically, in 256ths of a pixel; the position's whole pixels (to 255) plus the screen effect's offset go to the layer's scroll registers. When a movement's frame count runs out the layer moves to the next of its four movement slots, back to the first after the fourth; an empty slot also sends it back to the first, and if that is empty too, no movement loads and the old speed and acceleration keep running. A count of 0 never runs out. CodeA distorting layer's scroll register gets a new value on every one of the 224 visible scanlines: the frame routine fills a 224-entry buffer, and an indirect HDMA channel in repeat mode writes the line's two bytes to the layer's horizontal scroll register (or, for the vertical kind, its vertical one) as each line is drawn. Each frame the distortion's frequency, amplitude, start phase, and compression change by their per-frame steps before the buffer is refilled; when the second layer exists and its first distortion slot isn't empty, each layer's buffer is refilled only every other frame, the two layers taking turns by the frame counter's low bit. When a distortion's frame count runs out the next of the layer's four distortion slots starts; an empty slot sends it back to the first, and if that is empty too, no distortion loads and the current one keeps running. A count of 0 never runs out. CodeChecked by claude, 2026-11-08 — parsed every entry's 17 lines on the US branch at the pinned commit and compared each byte with the ROMRevised after a check by a second AI model, 2026-11-08.Scrolling and distortion columns list the entries of earthbound-av/scrolling and earthbound-av/distortions a layer plays in turn. Most IDs are the disassembly's placeholders (UNKNOWN001…). The fourth byte, which the table's comment leaves as ???, is the palette-cycling style the frame routine reads (earthbound-av/palette-cycling). An entry counts as cycling when its palette-cycling style isn't 0, as scrolling when any of its four scrolling slots isn't 0, and as distorting when any of its four distortion slots isn't 0. Each slot plays in turn as earthbound-av/scroll-motion and earthbound-av/distortion-hdma say.
The scrolling table holds 120 movements, 10 bytes each: how many frames the movement lasts (0 for as long as the layer shows), and a horizontal and vertical speed and acceleration, in 256ths of a pixel a frame. earthbound-av/scrolling
src/data/battle/backgrounds/scrolling_table.asm#L1-L123
;Duration, Horizontal movement, Vertical movement, Horizontal acceleration, Vertical acceleration
BG_SCROLLING_TABLE:
.WORD $0000, $0000, $0000, $0000, $0000
.WORD $00B4, $FF00, $0100, $0000, $0000
.WORD $00B4, $0100, $0100, $0000, $0000
…EarthBound (USA) · 0xaf258-0xaf707 — $CA:F258-$CA:F707: BG_SCROLLING_TABLE, 10 bytes a movementChecked by claude, 2026-11-08 — parsed the table on the US branch at the pinned commit; read how the frame routine applies it (earthbound-av/scroll-motion)Checked by a second AI model, 2026-11-08.Values are signed: $FF00 is −256, one pixel a frame left or up.
The distortion table holds 135 distortions, 17 bytes each: how many frames it lasts (0 for as long as the layer shows), its kind (none, horizontal, interlaced, vertical, or a fourth kind, earthbound-av/distortion-kind-4), and a ripple frequency, ripple amplitude, starting phase, and compression rate, each with a change applied every frame. earthbound-av/distortions
src/data/battle/backgrounds/distortion_table.asm#L1-L1486
BG_DISTORTION_TABLE:
.WORD $0000 ;Duration
.BYTE DISTORTION_STYLE::NONE
.WORD $0000 ;Ripple frequency
.WORD $0000 ;Ripple amplitude
.BYTE $00 ;Unknown
.WORD $0000 ;Compression rate
.WORD $0000 ;Ripple frequency acceleration
.WORD $0000 ;Ripple amplitude acceleration
.BYTE $00 ;Speed
.WORD $0000 ;Compression rate acceleration
…ebsrc: Source code recreation for the game Earthbound · include/structs.asm#L426-L437.STRUCT distortion_entry
duration .word ;0
style .byte ;2
ripple_frequency .word ;3
ripple_amplitude .word ;5
speed .byte ;7
compression_rate .word ;8
ripple_frequency_acceleration .word ;10
ripple_amplitude_acceleration .word ;12
speed_acceleration .byte ;14
compression_acceleration .word;15
.ENDSTRUCTebsrc: Source code recreation for the game Earthbound · include/enums.asm#L331-L337.ENUM DISTORTION_STYLE
NONE = 0
HORIZONTAL_SMOOTH = 1
HORIZONTAL_INTERLACED = 2
VERTICAL_SMOOTH = 3
UNKNOWN = 4
.ENDENUMEarthBound (USA) · 0xaf708-0xafffe — $CA:F708-$CA:FFFE: BG_DISTORTION_TABLE, 17 bytes a distortionA distorting layer's scroll register gets a new value on every one of the 224 visible scanlines: the frame routine fills a 224-entry buffer, and an indirect HDMA channel in repeat mode writes the line's two bytes to the layer's horizontal scroll register (or, for the vertical kind, its vertical one) as each line is drawn. Each frame the distortion's frequency, amplitude, start phase, and compression change by their per-frame steps before the buffer is refilled; when the second layer exists and its first distortion slot isn't empty, each layer's buffer is refilled only every other frame, the two layers taking turns by the frame counter's low bit. When a distortion's frame count runs out the next of the layer's four distortion slots starts; an empty slot sends it back to the first, and if that is empty too, no distortion loads and the current one keeps running. A count of 0 never runs out. CodeThe horizontal kind (1) shifts each scanline sideways by a sine wave: line by line, the offset is the layer's horizontal scroll plus the sine of the phase times the amplitude's high byte ÷ 256; the phase starts at the start phase plus the target layer's vertical scroll and moves on by the frequency (in 256ths of a sine step) each line. The sine table has 256 entries, one turn. As the start phase changes from frame to frame, the wave travels up or down the screen. CodeChecked by claude, 2026-11-08 — parsed every entry's ten lines on the US branch at the pinned commit and compared the duration, kind, frequency, amplitude, phase, and phase change with the ROM's 17 bytes per entry; the audit of 2026-11-08 compared the remaining columns with the ROM and found them matchingRevised after a check by a second AI model, 2026-11-08.The table's comments call the eighth byte Unknown and the fifteenth Speed; the structure names them speed and speed_acceleration, and the frame routine passes the first as the sine table's starting index and adds the second to it every frame, so here they are the start phase and its change a frame. earthbound-av/distortion-kind-4 says what the fourth kind does.
Each of the 484 battle groups names two background layers from the layer table, either of which may be none; 52 use both and the rest one or none. earthbound-av/battle-backgrounds
src/data/battle/background_layer_table.asm#L1-L486
BTL_ENTRY_BG_TABLE:
.WORD BATTLEBG_LAYER::NONE, BATTLEBG_LAYER::NONE ;0
.WORD BATTLEBG_LAYER::UNKNOWN262, BATTLEBG_LAYER::NONE ;1
.WORD BATTLEBG_LAYER::UNKNOWN260, BATTLEBG_LAYER::NONE ;2
.WORD BATTLEBG_LAYER::UNKNOWN261, BATTLEBG_LAYER::NONE ;3
…EarthBound (USA) · 0xbd89a-0xbe029 — $CB:D89A-$CB:E029: BTL_ENTRY_BG_TABLE, two layer numbers a group, 484 groupsChecked by claude, 2026-11-08 — parsed the table on the US branch at the pinned commitRevised after a check by a second AI model, 2026-11-08.Group numbers match earthbound-tables/battle-groups. Layer numbers index earthbound-av/layers.
Every frame, each background layer counts down its palette timer (a timer at 0 never runs); when it runs out it is reset to the layer's frames-a-step value and the layer's colors move one step. Style 1 rotates the colors of cycle 1's range by one place, wrapping at its end; style 2 rotates cycle 2's range the same way and then cycle 1's; style 3 runs cycle 1's range forth and back, the colors sliding one way and then mirrored back over twice the range's length. Style 0 doesn't cycle. earthbound-av/palette-cycling
About: EarthBound Palette cycling
Codeebsrc: Source code recreation for the game Earthbound ·src/misc/battlebgs/generate_frame.asm#L23-L59LDY #loaded_bg_data::freeze_palette_scrolling
LDA (@LOCAL07),Y
AND #$00FF
BNEL @TARGET_BG_LAYER_SELECTION_COMPLETE
LDA @LOCAL07
CLC
ADC #loaded_bg_data::palette_change_duration_left
TAX
SEP #PROC_FLAGS::ACCUM8
LDA __BSS_START__,X
STA @LOCAL05
REP #PROC_FLAGS::ACCUM8
…Our readinga frozen layer skips cycling and scrolling23–26count the timer down; unless it reaches 0, skip27–43reset it to the frames-a-step value44–47branch on the style: 2, 1, 3, else none48–58ebsrc: Source code recreation for the game Earthbound · src/misc/battlebgs/generate_frame.asm#L143-L227@PALETTE_SHIFTING_STYLE1:
LDY #loaded_bg_data::palette_cycle_1_last
SEP #PROC_FLAGS::ACCUM8
LDA (@LOCAL07),Y
LDY #loaded_bg_data::palette_cycle_1_first
SEC
SBC (@LOCAL07),Y
REP #PROC_FLAGS::ACCUM8
AND #$00FF
STA @VIRTUAL02
INC @VIRTUAL02
LDX #0
…Our readingn = the range's last color − first + 1143–153for each color i of the range157 source = i − step, plus n when i is below the step158–176 color i = the layer's stored color at the source177–207step += 1, back to 0 at n212–227ebsrc: Source code recreation for the game Earthbound · src/misc/battlebgs/generate_frame.asm#L228-L338@PALETTE_SHIFTING_STYLE3:
LDY #loaded_bg_data::palette_cycle_1_last
SEP #PROC_FLAGS::ACCUM8
LDA (@LOCAL07),Y
LDY #loaded_bg_data::palette_cycle_1_first
SEC
SBC (@LOCAL07),Y
REP #PROC_FLAGS::ACCUM8
AND #$00FF
INC
STA @LOCAL01
LDY #0
…Our readingn = the range's length228–238for each color i: k = i + step; past 2n, less 2n; past n, mirrored (2n − 1 − k)242–280 color i = the stored color at k281–310step += 1, back to 0 at 2n316–337ebsrc: Source code recreation for the game Earthbound · src/misc/battlebgs/generate_frame.asm#L59-L143@PALETTE_SHIFTING_STYLE2:
LDY #loaded_bg_data::palette_cycle_2_last
SEP #PROC_FLAGS::ACCUM8
LDA (@LOCAL07),Y
LDY #loaded_bg_data::palette_cycle_2_first
SEC
SBC (@LOCAL07),Y
REP #PROC_FLAGS::ACCUM8
AND #$00FF
STA @VIRTUAL02
INC @VIRTUAL02
LDX #0
…Checked by claude, 2026-11-08 — read GENERATE_BATTLEBG_FRAME's palette part on the US branch at the pinned commit; style 2 runs cycle 2's loop and then style 1'sRevised after a check by a second AI model, 2026-11-08.A step copies colors from the layer's stored palette into the live one, so a cycle never drifts. The per-entry styles and ranges are in earthbound-av/layers.
Each frame, unless the layer's freeze flag is set, a background layer's speed grows by its acceleration and its position by its speed, horizontally and vertically, in 256ths of a pixel; the position's whole pixels (to 255) plus the screen effect's offset go to the layer's scroll registers. When a movement's frame count runs out the layer moves to the next of its four movement slots, back to the first after the fourth; an empty slot also sends it back to the first, and if that is empty too, no movement loads and the old speed and acceleration keep running. A count of 0 never runs out. earthbound-av/scroll-motion
src/misc/battlebgs/generate_frame.asm#L346-L555LDA @LOCAL07
CLC
ADC #loaded_bg_data::scrolling_duration_left
TAX
LDA __BSS_START__,X
BEQL @UNKNOWN29
DEC
STA __BSS_START__,X
BNEL @UNKNOWN29
…
STX @LOCAL00
SEP #PROC_FLAGS::ACCUM8
…Our readingcount the movement's frames down; when they run out348–354next of the four slots; an empty slot goes back to the first357–388load the movement's frames, speeds, and accelerations389–445speed += acceleration; position += speed, horizontally446–467and vertically468–486pick the layer the result goes to487–496ebsrc: Source code recreation for the game Earthbound · src/misc/battlebgs/generate_frame.asm#L23-L26LDY #loaded_bg_data::freeze_palette_scrolling
LDA (@LOCAL07),Y
AND #$00FF
BNEL @TARGET_BG_LAYER_SELECTION_COMPLETEChecked by claude, 2026-11-08 — read GENERATE_BATTLEBG_FRAME's scrolling part on the US branch at the pinned commitRevised after a check by a second AI model, 2026-11-08.Scrolling and palette cycling stop once Giygas is defeated. The movements are earthbound-av/scrolling.
A distorting layer's scroll register gets a new value on every one of the 224 visible scanlines: the frame routine fills a 224-entry buffer, and an indirect HDMA channel in repeat mode writes the line's two bytes to the layer's horizontal scroll register (or, for the vertical kind, its vertical one) as each line is drawn. Each frame the distortion's frequency, amplitude, start phase, and compression change by their per-frame steps before the buffer is refilled; when the second layer exists and its first distortion slot isn't empty, each layer's buffer is refilled only every other frame, the two layers taking turns by the frame counter's low bit. When a distortion's frame count runs out the next of the layer's four distortion slots starts; an empty slot sends it back to the first, and if that is empty too, no distortion loads and the current one keeps running. A count of 0 never runs out. earthbound-av/distortion-hdma
src/misc/battlebgs/generate_frame.asm#L556-L822@TARGET_BG_LAYER_SELECTION_COMPLETE:
LDA @LOCAL07
CLC
ADC #loaded_bg_data::distortion_duration_left
TAX
LDA __BSS_START__,X
BEQL @DISTORTION_DMA_DONE
DEC
STA __BSS_START__,X
BNEL @DISTORTION_DMA_DONE
LDA @LOCAL07
CLC
…Our readingcount the distortion's frames down; when they run out, the next of four slots557–599load the entry's kind, frequency, amplitude, phase, compression, and their steps600–715the vertical kind sends the buffer to the vertical scroll register, the rest to the horizontal717–737none: stop738–745frequency, amplitude, phase, and compression each += their step748–785pass the kind − 1 and the compression to the buffer routine786–798at 30 frames a second, refill only on this layer's frames799–805fill the buffer from frequency, amplitude's high byte, and phase806–820ebsrc: Source code recreation for the game Earthbound · src/misc/battlebgs/do_battlebg_dma.asm#L1-L53
DO_BATTLEBG_DMA:
PHY
TAY
LDA f:DMA_TARGET_REGISTERS,X
PHA
TYA
ASL
ASL
ASL
ASL
TAX
…Our readingthe channel's target is the layer's scroll register5–6mode $42: indirect, two writes to one register20–21copy the HDMA table for layer 1 or 225–43start the channel with the frame's HDMA45–51ebsrc: Source code recreation for the game Earthbound · src/data/unknown/C0AE26.asm#L2-L8;an HDMA table (indirect) for battle backgrounds
UNKNOWN_C0AE26:
.BYTE $E4
.WORD .LOWORD(ANIMATED_BACKGROUND_LAYER_1_HDMA_BUFFER)
.BYTE $FC
.WORD .LOWORD(ANIMATED_BACKGROUND_LAYER_1_HDMA_BUFFER) + 200
.BYTE $00ebsrc: Source code recreation for the game Earthbound · src/data/dma_target_registers.asm#L1-L3
DMA_TARGET_REGISTERS:
.LOBYTES WMDATA, BG1HOFS, BG2HOFS, BG3HOFS, BG4HOFS, BG1VOFS, BG2VOFS, BG3VOFS, BG4VOFSebsrc: Source code recreation for the game Earthbound · src/battle/load_battlebg.asm#L706-L717@UNKNOWN23:
REP #PROC_FLAGS::ACCUM8
STZ DISTORT_30FPS
LDA LOADED_BG_DATA_LAYER2
AND #$00FF
BEQ @UNKNOWN24
LDA LOADED_BG_DATA_LAYER2 + loaded_bg_data::distortion_styles
AND #$00FF
BEQ @UNKNOWN24
LDA #1
STA DISTORT_30FPS
@UNKNOWN24:Checked by claude, 2026-11-08 — read GENERATE_BATTLEBG_FRAME's distortion part, DO_BATTLEBG_DMA, its HDMA table, and the target registers on the US branch at the pinned commitRevised after a check by a second AI model, 2026-11-08.The HDMA table hands the channel 100 lines from the buffer's start and then 124 from 200 bytes in, two bytes a line ($E4 and $FC are repeat counts of 100 and 124).
The horizontal kind (1) shifts each scanline sideways by a sine wave: line by line, the offset is the layer's horizontal scroll plus the sine of the phase times the amplitude's high byte ÷ 256; the phase starts at the start phase plus the target layer's vertical scroll and moves on by the frequency (in 256ths of a sine step) each line. The sine table has 256 entries, one turn. As the start phase changes from frame to frame, the wave travels up or down the screen. earthbound-av/distortion-horizontal
src/misc/battlebgs/prepare_bg_offset_tables.asm#L2-L34PREPARE_BG_OFFSET_TABLES:
PHD
PHA
TDC
SEC
SBC #$000B
AND #$FF00
TCD
PLA
STA $00
TXA
SEP #PROC_FLAGS::ACCUM8
…Our readingthe amplitude goes to the multiplier; phase = the start phase, in 256ths11–19one layer's buffer is 224 lines; the second layer's follows it21–29the kind picks the loop: 0 and 1, 2, or 330–34ebsrc: Source code recreation for the game Earthbound · src/misc/battlebgs/prepare_bg_offset_tables.asm#L35-L70@UNKNOWN2:
LDA BACKGROUND_DISTORTION_TARGET_LAYER
BEQ @UNKNOWN3
DEC
ASL
ASL
TAX
LDA __BSS_START__+51,X
CLC
ADC $03
AND #$00FF
STA $03
…Our readingwith a target layer: phase += its vertical scroll; base = its horizontal scroll36–48kind 0: for each line50–53 offset = base + sine(phase) × amplitude ÷ 25654–60 phase += frequency61–64until the buffer is full65–68ebsrc: Source code recreation for the game Earthbound · src/data/sine_table.asm#L1-L258SINE_LOOKUP_TABLE:
.BYTE $00
.BYTE $03Checked by claude, 2026-11-08 — read PREPARE_BG_OFFSET_TABLES on the US branch at the pinned commit; the product is the PPU multiplier's (M7A × M7B, read from MPYM)Revised after a check by a second AI model, 2026-11-08.Distortion kinds are the table's numbers less 1 in this routine. Which layer's scroll the target layer names is read from the scroll mirrors at $31 and $33 (the disassembly's __BSS_START__+49 and +51).
The interlaced kind (2) draws the same sine wave as the horizontal kind but alternates its sign: even scanlines move by + the wave and odd ones by −, so neighboring lines pull apart in opposite directions. earthbound-av/distortion-interlaced
src/misc/battlebgs/prepare_bg_offset_tables.asm#L2-L34PREPARE_BG_OFFSET_TABLES:
PHD
PHA
TDC
SEC
SBC #$000B
AND #$FF00
TCD
PLA
STA $00
TXA
SEP #PROC_FLAGS::ACCUM8
…Our readingthe amplitude goes to the multiplier; phase = the start phase, in 256ths11–19one layer's buffer is 224 lines; the second layer's follows it21–29the kind picks the loop: 0 and 1, 2, or 330–34ebsrc: Source code recreation for the game Earthbound · src/misc/battlebgs/prepare_bg_offset_tables.asm#L35-L103@UNKNOWN2:
LDA BACKGROUND_DISTORTION_TARGET_LAYER
BEQ @UNKNOWN3
DEC
ASL
ASL
TAX
LDA __BSS_START__+51,X
CLC
ADC $03
AND #$00FF
STA $03
…Our readingwith a target layer: phase += its vertical scroll; base = its horizontal scroll36–48kind 1: for each pair of lines71–73 first: base + sine(phase) × amplitude ÷ 256; phase += frequency74–84 second: base − sine(phase) × amplitude ÷ 256; phase += frequency85–95until the buffer is full96–103ebsrc: Source code recreation for the game Earthbound · src/misc/battlebgs/prepare_bg_offset_tables.asm#L71-L95@UNKNOWN5:
LDY $07
@UNKNOWN6:
LDX $03
LDA f:SINE_LOOKUP_TABLE,X
STA f:M7B
LDA f:MPYM
CLC
ADC $05
STA ANIMATED_BACKGROUND_LAYER_1_HDMA_BUFFER,Y
LDA $02
CLC
…Checked by claude, 2026-11-08 — read PREPARE_BG_OFFSET_TABLES on the US branch at the pinned commitRevised after a check by a second AI model, 2026-11-08.The vertical kind (3) writes the vertical scroll register line by line: an accumulator starts at the target layer's vertical scroll × 256 and grows by the compression rate before each line's value is taken, and each line gets the accumulator's whole part (to 255) plus the sine wave. With a compression of 0 every line gets the layer's vertical scroll plus the wave; a rate r adds (line + 1) × r ÷ 256 more, line by line, in 256ths of a row. earthbound-av/distortion-vertical
src/misc/battlebgs/prepare_bg_offset_tables.asm#L2-L34PREPARE_BG_OFFSET_TABLES:
PHD
PHA
TDC
SEC
SBC #$000B
AND #$FF00
TCD
PLA
STA $00
TXA
SEP #PROC_FLAGS::ACCUM8
…Our readingthe amplitude goes to the multiplier; phase = the start phase, in 256ths11–19one layer's buffer is 224 lines; the second layer's follows it21–29the kind picks the loop: 0 and 1, 2, or 330–34ebsrc: Source code recreation for the game Earthbound · src/misc/battlebgs/prepare_bg_offset_tables.asm#L104-L139@UNKNOWN7:
LDA BACKGROUND_DISTORTION_TARGET_LAYER
BEQ @UNKNOWN8
DEC
ASL
ASL
TAX
LDA __BSS_START__+51,X
@UNKNOWN8:
XBA
AND #$FF00
STA $05
…Our readingstart the accumulator at the target layer's vertical scroll × 256105–115for each line: accumulator += compression117–124 offset = the accumulator's high byte + sine(phase) × amplitude ÷ 256125–128 phase += frequency, until the buffer is full129–139ebsrc: Source code recreation for the game Earthbound · src/misc/battlebgs/prepare_bg_offset_tables.asm#L104-L137@UNKNOWN7:
LDA BACKGROUND_DISTORTION_TARGET_LAYER
BEQ @UNKNOWN8
DEC
ASL
ASL
TAX
LDA __BSS_START__+51,X
@UNKNOWN8:
XBA
AND #$FF00
STA $05
…Checked by claude, 2026-11-08 — read PREPARE_BG_OFFSET_TABLES on the US branch at the pinned commitRevised after a check by a second AI model, 2026-11-08.The fourth kind (4), unnamed in the disassembly, runs the vertical kind's sums (the compression accumulator plus the wave) with the interlaced kind's alternating sign, but its buffer goes to the horizontal scroll register, as every kind but 3 does. So neighboring lines wave apart sideways as in the interlaced kind, around a base that the routine takes from the target layer's vertical scroll, and a compression rate shears the layer sideways one step further each line. earthbound-av/distortion-kind-4
src/misc/battlebgs/prepare_bg_offset_tables.asm#L2-L34PREPARE_BG_OFFSET_TABLES:
PHD
PHA
TDC
SEC
SBC #$000B
AND #$FF00
TCD
PLA
STA $00
TXA
SEP #PROC_FLAGS::ACCUM8
…Our readingthe amplitude goes to the multiplier; phase = the start phase, in 256ths11–19one layer's buffer is 224 lines; the second layer's follows it21–29the kind picks the loop: 0 and 1, 2, or 330–34ebsrc: Source code recreation for the game Earthbound · src/misc/battlebgs/prepare_bg_offset_tables.asm#L140-L193@UNKNOWN10:
LDA BACKGROUND_DISTORTION_TARGET_LAYER
BEQ @UNKNOWN11
DEC
ASL
ASL
TAX
LDA __BSS_START__+51,X
@UNKNOWN11:
XBA
AND #$FF00
STA $05
…Our readingstart the accumulator at the target layer's vertical scroll × 256141–151for each pair of lines: accumulator += compression; first = its high byte + the wave153–168 accumulator += compression; second = its high byte − the wave169–184until the buffer is full185–193ebsrc: Source code recreation for the game Earthbound · src/misc/battlebgs/generate_frame.asm#L717-L737 CMP #DISTORTION_STYLE::VERTICAL_SMOOTH
BNE @HORIZONTAL_DISTORTION
LDY @LOCAL08
LDX @LOCAL06
INX
INX
INX
INX
LDA @LOCAL08
CLC
ADC #5
JSL DO_BATTLEBG_DMA
…ebsrc: Source code recreation for the game Earthbound · src/misc/battlebgs/prepare_bg_offset_tables.asm#L153-L184@UNKNOWN12:
LDX $03
LDA f:SINE_LOOKUP_TABLE,X
STA f:M7B
LDA $05
CLC
ADC BACKGROUND_DISTORTION_COMPRESSION_RATE
STA $05
XBA
AND #$00FF
CLC
ADC f:MPYM
…Checked by claude, 2026-11-08 — read PREPARE_BG_OFFSET_TABLES and the frame routine's register choice on the US branch at the pinned commitRevised after a check by a second AI model, 2026-11-08.26 entries of the distortion table are kind 4, 2 of them with a compression rate.
The battle swirl is one of 6 swirl animations (entry 0 is empty), each a run of window shapes from a list of 126 HDMA window tables: swirl 1 shows shapes 0–22, a new one every 2 frames, and swirl 3 shows shapes 38–59, a new one every 3 frames. Battle groups from 448 on use swirl 3, and every other battle uses swirl 1. earthbound-av/battle-swirl
About: EarthBound HDMA
Codeebsrc: Source code recreation for the game Earthbound ·src/bankconfig/common/bank0e.asm#L464-L465SWIRL_PRIMARY_TABLE:
.BYTE $00, $00, $00, $00, $02, $00, $17, $00, $04, $17, $0F, $00, $03, $26, $16, $00, $04, $3C, $15, $00, $02, $51, $1C, $00, $03, $6D, $11, $00Our readingeach 4 bytes: frames per shape, first shape, shape count, 0465EarthBound (USA) · 0xedd41-0xedd5c — the swirl table, 7 four-byte entriesebsrc: Source code recreation for the game Earthbound · src/unknown/C4/C4A67E.asm#L11-L86 STX @OPTIONS
STA @VIRTUAL04
LDA @OPTIONS
AND #$0002
BEQ @UNKNOWN0
SEP #PROC_FLAGS::ACCUM8
…
ADC @VIRTUAL06
STA @VIRTUAL06
SEP #PROC_FLAGS::ACCUM8
LDA [@VIRTUAL06]
STA @LOCAL00
…Our readingoptions bit 0 → SWIRL_REVERSED; entry → FRAMES_UNTIL_NEXT_SWIRL_FRAME, SWIRL_FRAMES_LEFT, SWIRL_HDMA_TABLE_ID52–86ebsrc: Source code recreation for the game Earthbound · src/data/battle/swirl_pointers.asm#L1-L128
SWIRL_POINTER_TABLE:
.WORD .LOWORD(SWIRL_DATA_0)
.WORD .LOWORD(SWIRL_DATA_1)
…ebsrc: Source code recreation for the game Earthbound · src/overworld/battle_swirl_sequence.asm#L12-L96LDA #$0001
STA $16
…
@UNKNOWN3:
LDA CURRENT_BATTLE_GROUP
CMP #ENEMY_GROUP::BOSS_FRANK
BCC @UNKNOWN4
LDA #$0003
STA $16
…
LDY #$001E
LDX $02
…Our readinginitiative → color and music; group ≥ BOSS_FRANK → swirl 312–96ebsrc: Source code recreation for the game Earthbound · include/constants/enemy_groups.asm#L451-L451BOSS_FRANK = 448ebsrc: Source code recreation for the game Earthbound · src/battle/init_scripted.asm#L51-L51JSL BATTLE_SWIRL_SEQUENCEChecked by claude, 2026-11-08 — Parsed SWIRL_PRIMARY_TABLE (src/bankconfig/common/bank0e.asm) and found its 28 bytes once in the ROM; checked that the shape counts sum to the 126 entries of SWIRL_POINTER_TABLE and that the runs are contiguous; read battle_swirl_sequence.asm, C2E8C4.asm, and C4A67E.asm; scratch/swirl.mjs.Revised after a check by a second AI model, 2026-11-08.Other code starts the remaining swirls: the battle's opening animation (start_battle_animation.asm) starts swirls 5, 4, and 2, the final battle's scene changes start swirl 6, and the screen transitions pass a swirl number from their own data. The 126 window tables are data lines in swirl_pointers.asm's bank; their format is not read here.
Cited by: Battle transition
During the battle swirl the screen subtracts a fixed color inside the color window. The swirl sets that color to red 4, green 4, blue 0 for a normal battle; red 28, green 5, blue 12 when the party strikes first; and red 0, green 31, blue 31 when the enemies strike first. It halves the result for a normal battle and for battle groups from 448 on, whoever strikes first. earthbound-av/battle-swirl-tint
src/overworld/battle_swirl_sequence.asm#L12-L91LDA #$0001
STA $16
LDA #$0004
STA @SWIRL_RED
STA @SWIRL_GREEN
LDY #$0000
STY @SWIRL_BLUE
LDA BATTLE_INITIATIVE
BEQ @UNKNOWN0
CMP #INITIATIVE::PARTY_FIRST
BEQ @UNKNOWN1
CMP #INITIATIVE::ENEMIES_FIRST
…ebsrc: Source code recreation for the game Earthbound · include/hardware.asm#L70-L94.ENUM CGWSEL_BITS
…
COLOUR_MATH_ENABLE_ALWAYS = 0 << 4
COLOUR_MATH_ENABLE_MATHWIN = 1 << 4
…
SUBSCREEN_BGOBJ_ENABLE = 1 << 1
SUBSCREEN_BGOBJ_DISABLE = 0 << 1
…
.ENUM CGADSUB_BITS
COLOUR_MATH_ADDSUB = 1 << 7
COLOUR_MATH_DIV2 = 1 << 6ebsrc: Source code recreation for the game Earthbound · include/constants/enemy_groups.asm#L451-L451BOSS_FRANK = 448PPU registers · CGADSUB - Color math designation ($2131 write)+--------- Operator type (0 = add, 1 = subtract)PPU registers · CGADSUB - Color math designation ($2131 write)|+-------- Half color mathPPU registers · CGWSEL - Color addition select ($2130 write)|||| +-- Addend (0 = fixed color, 1 = subscreen)PPU registers · CGWSEL - Color addition select ($2130 write)||++------ Sub screen color window transparent regionPPU registers · CGWSEL - Color addition select ($2130 write)1 |Outside color windowPPU registers · CGWSEL - Color addition select ($2130 write)The sub-screen setting is for masking color math.Checked by claude, 2026-11-08 — read battle_swirl_sequence.asm's initiative and group tests and its SET_COLDATA and SET_COLOUR_ADDSUB_MODE calls on the US branch at the pinned commit, the CGWSEL and CGADSUB bit names in hardware.asm, and what those bits do in the SNESdev Wiki's PPU registers page: subtract (bit 7) and half (bit 6) set, the fixed color as addend, and the sub-screen side made transparent outside the color windowRevised after a check by a second AI model, 2026-11-08.Cited by: Battle transition, Color math
The battle swirl plays song 176 for a normal battle or a party first strike, song 9 for an enemy first strike, and song 8 for battle groups from 448 on. earthbound-av/battle-swirl-music
src/overworld/battle_swirl_sequence.asm#L19-L71LDA BATTLE_INITIATIVE
BEQ @UNKNOWN0
CMP #INITIATIVE::PARTY_FIRST
BEQ @UNKNOWN1
CMP #INITIATIVE::ENEMIES_FIRST
BEQ @UNKNOWN2
BRA @UNKNOWN3
@UNKNOWN0:
LDX #MUSIC::BATTLE_SWIRL4
STX @SWIRL_MUSIC
…
@UNKNOWN1:
…ebsrc: Source code recreation for the game Earthbound · include/constants/music.asm#L10-L11BATTLE_SWIRL1 = 8
BATTLE_SWIRL2 = 9ebsrc: Source code recreation for the game Earthbound · include/constants/music.asm#L178-L178BATTLE_SWIRL4 = 176ebsrc: Source code recreation for the game Earthbound · include/constants/enemy_groups.asm#L451-L451BOSS_FRANK = 448The song table has 191 entries, one for each song number from 1 to 191; each is three bytes, the primary sample pack, the secondary sample pack, and the sequence pack that the sound driver loads (255 skips that load). This is the game's track listing, by song number, with where the game plays each, from every request found: the music byte of each enemy (the battle's first enemy sets the battle music), the map's music lists that the 2560 map sectors point to, the Sound Stone's list, the song each final-battle scene is passed, the calls to the music-change routine in the code, and the music control codes in the text scripts, battle messages included. 186 entries are requested; no request found reaches 10, 109, 133, 180, 189. These are candidates for unused, not findings: the debug sound menu, where it can be reached, steps through every number from 0 to 191, and a text control code with a zero argument takes its song from a value the script set earlier. CodeChecked by claude, 2026-11-08 — read battle_swirl_sequence.asm's song choices and its CHANGE_MUSIC call on the US branch at the pinned commit, and the song numbers in music.asmRevised after a check by a second AI model, 2026-11-08.