From 7b94c7c32f73947ccc4cccdae73211bf924344f9 Mon Sep 17 00:00:00 2001 From: idle Date: Wed, 8 Jul 2026 18:27:35 -0500 Subject: [PATCH 1/5] Fix OOB xray aiming up crash caused by missing %a16() calls --- src/preserve_wram.asm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/preserve_wram.asm b/src/preserve_wram.asm index 5139bd16..20e6fb0f 100644 --- a/src/preserve_wram.asm +++ b/src/preserve_wram.asm @@ -1498,6 +1498,7 @@ xray_offscreen_up_right: .rightLostThirdLoop LDA #$FF : STA [$00],Y DEY #2 : BPL .rightLostThirdLoop + %a16() .rightEdgeEnd LDY $12 @@ -1739,6 +1740,7 @@ xray_offscreen_up_left: .rightLostThirdLoop LDA #$00 : STA [$00],Y DEY #2 : BPL .rightLostThirdLoop + %a16() .rightEdgeEnd LDY $14 From a5c78434c7588435b44915a99cdab109efc20b69 Mon Sep 17 00:00:00 2001 From: idle Date: Wed, 8 Jul 2026 18:45:32 -0500 Subject: [PATCH 2/5] Use !OAM_LOW and !OAM_HIGH variables for writing sprites --- src/spritefeat.asm | 202 ++++++++++++++++++++++----------------------- 1 file changed, 101 insertions(+), 101 deletions(-) diff --git a/src/spritefeat.asm b/src/spritefeat.asm index b3e9b212..9e42e18c 100644 --- a/src/spritefeat.asm +++ b/src/spritefeat.asm @@ -181,21 +181,21 @@ draw_sprite_oob: ; Set sprite ID %a8() - LDY !OAM_STACK_POINTER : STA $0372,Y + LDY !OAM_STACK_POINTER : STA !OAM_LOW+$2,Y ; Get X coord LDA $C1 : INC #2 : ASL #4 : SEC : SBC $C5 - STA $0370,Y + STA !OAM_LOW,Y ; Get Y coord LDA $C3 : CLC : ADC #$04 : ASL #4 : SEC : SBC $C7 - STA $0371,Y + STA !OAM_LOW+$1,Y ; Sprite Attributes - xxxxxxxx yyyyyyyy YXPPpppt tttttttt ; x=X pos, y=Y pos (low nibbles only), Y=Y flip, X=X flip ; P=Priority, p=Palette, t=Tile number ; Priority bits set, palette = 101 - LDA #%00111010 : STA $0373,Y + LDA #%00111010 : STA !OAM_LOW+$3,Y %a16() @@ -249,7 +249,7 @@ draw_sprite_oob: ; b: sx for sprite 4n+1 ; c: sx for sprite 4n+2 ; d: sx for sprite 4n+3 - LDA #%10101010 : STA $0570,X + LDA #%10101010 : STA !OAM_HIGH,X INX : CPX $CB : BNE .copy_loop %ai16() @@ -284,28 +284,28 @@ draw_oob_samus_hitbox: LDY !OAM_STACK_POINTER PLA ; X coord SEC : SBC !SAMUS_X_RADIUS - STA $0370,Y : STA $0378,Y + STA !OAM_LOW,Y : STA !OAM_LOW+$8,Y CLC : ADC !SAMUS_X_RADIUS : ADC !SAMUS_X_RADIUS : SEC : SBC #$08 - STA $0374,Y : STA $037C,Y + STA !OAM_LOW+$4,Y : STA !OAM_LOW+$C,Y PLA ; discard high byte PLA : DEC ; Y coord SEC : SBC !SAMUS_Y_RADIUS - STA $0371,Y : STA $0375,Y + STA !OAM_LOW+$1,Y : STA !OAM_LOW+$5,Y CLC : ADC !SAMUS_Y_RADIUS : ADC !SAMUS_Y_RADIUS : SEC : SBC #$08 - STA $0379,Y : STA $037D,Y + STA !OAM_LOW+$9,Y : STA !OAM_LOW+$D,Y PLA ; discard high byte LDA #%00111010 - STA $0373,Y ; Sprite 1 ATTR - STA $0377,Y ; Sprite 2 ATTR - STA $037B,Y ; Sprite 3 ATTR - STA $037F,Y ; Sprite 4 ATTR + STA !OAM_LOW+$3,Y ; Sprite 1 ATTR + STA !OAM_LOW+$7,Y ; Sprite 2 ATTR + STA !OAM_LOW+$B,Y ; Sprite 3 ATTR + STA !OAM_LOW+$F,Y ; Sprite 4 ATTR - LDA #$DC : STA $0372,Y - LDA #$DD : STA $0376,Y - LDA #$EC : STA $037A,Y - LDA #$ED : STA $037E,Y + LDA #$DC : STA !OAM_LOW+$2,Y + LDA #$DD : STA !OAM_LOW+$6,Y + LDA #$EC : STA !OAM_LOW+$A,Y + LDA #$ED : STA !OAM_LOW+$E,Y ; Normally the high sprite bits are cleared to zero so this shouldn't be needed for 8x8 sprites, ; but the hitbox drawing code will overwrite 1-3 extra sprite bits to gain speed so instead we @@ -320,7 +320,7 @@ draw_oob_samus_hitbox: TXA : AND #$0003 : ASL : TAX ; Mask off bits to clear high sprite offset - LDA $0570,Y : AND.l spr_clr_flags,X : STA $0570,Y + LDA !OAM_HIGH,Y : AND.l spr_clr_flags,X : STA !OAM_HIGH,Y ; Sprite number PLX : INX : TXA : PHX @@ -329,7 +329,7 @@ draw_oob_samus_hitbox: TXA : AND #$0003 : ASL : TAX ; Mask off bits to clear high sprite offset - LDA $0570,Y : AND.l spr_clr_flags,X : STA $0570,Y + LDA !OAM_HIGH,Y : AND.l spr_clr_flags,X : STA !OAM_HIGH,Y ; Sprite number PLX : INX : TXA : PHX @@ -338,7 +338,7 @@ draw_oob_samus_hitbox: TXA : AND #$0003 : ASL : TAX ; Mask off bits to clear high sprite offset - LDA $0570,Y : AND.l spr_clr_flags,X : STA $0570,Y + LDA !OAM_HIGH,Y : AND.l spr_clr_flags,X : STA !OAM_HIGH,Y ; Sprite number PLX : INX : TXA : PHX @@ -347,7 +347,7 @@ draw_oob_samus_hitbox: TXA : AND #$0003 : ASL : TAX ; Mask off bits to clear high sprite offset - LDA $0570,Y : AND.l spr_clr_flags,X : STA $0570,Y + LDA !OAM_HIGH,Y : AND.l spr_clr_flags,X : STA !OAM_HIGH,Y PLX : PLY ; Inc OAM stack @@ -370,29 +370,29 @@ draw_samus_hitbox: LDY !OAM_STACK_POINTER ; X coord PLA : SEC : SBC !SAMUS_X_RADIUS - STA $0370,Y : STA $0378,Y + STA !OAM_LOW,Y : STA !OAM_LOW+$8,Y CLC : ADC !SAMUS_X_RADIUS : ADC !SAMUS_X_RADIUS : SEC : SBC #$08 - STA $0374,Y : STA $037C,Y + STA !OAM_LOW+$4,Y : STA !OAM_LOW+$C,Y PLA ; discard high byte ; Y coord PLA : DEC : SEC : SBC !SAMUS_Y_RADIUS - STA $0371,Y : STA $0375,Y + STA !OAM_LOW+$1,Y : STA !OAM_LOW+$5,Y CLC : ADC !SAMUS_Y_RADIUS : ADC !SAMUS_Y_RADIUS : SEC : SBC #$08 - STA $0379,Y : STA $037D,Y + STA !OAM_LOW+$9,Y : STA !OAM_LOW+$D,Y PLA ; discard high byte ; Sprite Attributes - xxxxxxxx yyyyyyyy YXPPpppt tttttttt ; x=X pos, y=Y pos, Y=Y flip, X=X flip ; P=Priority, p=Palette, t=Tile number - LDA #%00111010 : STA $0373,Y ; top left - LDA #%01111010 : STA $0377,Y ; top right - LDA #%10111010 : STA $037B,Y ; bottom left - LDA #%11111010 : STA $037F,Y ; bottom right + LDA #%00111010 : STA !OAM_LOW+$3,Y ; top left + LDA #%01111010 : STA !OAM_LOW+$7,Y ; top right + LDA #%10111010 : STA !OAM_LOW+$B,Y ; bottom left + LDA #%11111010 : STA !OAM_LOW+$F,Y ; bottom right LDA #$47 ; tile number (8/9 bits) - STA $0372,Y : STA $0376,Y - STA $037A,Y : STA $037E,Y + STA !OAM_LOW+$2,Y : STA !OAM_LOW+$6,Y + STA !OAM_LOW+$A,Y : STA !OAM_LOW+$E,Y %a16() TYA : CLC : ADC #$0010 : STA !OAM_STACK_POINTER @@ -437,28 +437,28 @@ draw_enemy_hitbox: %a8() ; X coord PLA : SEC : SBC !ENEMY_X_RADIUS,X - STA $0370,Y : STA $0378,Y ; X pos + STA !OAM_LOW,Y : STA !OAM_LOW+$8,Y ; X pos CLC : ADC !ENEMY_X_RADIUS,X : ADC !ENEMY_X_RADIUS,X SEC : SBC #$08 - STA $0374,Y : STA $037C,Y + STA !OAM_LOW+$4,Y : STA !OAM_LOW+$C,Y PLA ; discard high byte ; Y coord PLA : DEC : SEC : SBC !ENEMY_Y_RADIUS,X - STA $0371,Y : STA $0375,Y + STA !OAM_LOW+$1,Y : STA !OAM_LOW+$5,Y CLC : ADC !ENEMY_Y_RADIUS,X : ADC !ENEMY_Y_RADIUS,X SEC : SBC #$08 - STA $0379,Y : STA $037D,Y + STA !OAM_LOW+$9,Y : STA !OAM_LOW+$D,Y PLA ; discard high byte ; Sprite Attributes - xxxxxxxx yyyyyyyy YXPPpppt tttttttt ; x=X pos, y=Y pos (low nibbles only), Y=Y flip, X=X flip ; P=Priority, p=Palette, t=Tile number %ai16() - LDA #$3A47 : STA $0372,Y ; %00111010 top-left - LDA #$7A47 : STA $0376,Y ; %01111010 top-right - LDA #$BA47 : STA $037A,Y ; %10111010 bottom-left - LDA #$FA47 : STA $037E,Y ; %11111010 bottom-right + LDA #$3A47 : STA !OAM_LOW+$2,Y ; %00111010 top-left + LDA #$7A47 : STA !OAM_LOW+$6,Y ; %01111010 top-right + LDA #$BA47 : STA !OAM_LOW+$A,Y ; %10111010 bottom-left + LDA #$FA47 : STA !OAM_LOW+$E,Y ; %11111010 bottom-right ; inc oam stack TYA : CLC : ADC #$0010 : STA !OAM_STACK_POINTER : TAY @@ -558,24 +558,24 @@ draw_ext_spritemap_hitbox: %a8() ; X coord LDA $1D : CLC : ADC $14 - STA $0370,Y : STA $0378,Y ; sprite X pos + STA !OAM_LOW,Y : STA !OAM_LOW+$8,Y ; sprite X pos LDA $1D : CLC : ADC $18 : SEC : SBC #$08 - STA $0374,Y : STA $037C,Y + STA !OAM_LOW+$4,Y : STA !OAM_LOW+$C,Y ; Y coord LDA $1C : DEC : CLC : ADC $16 - STA $0371,Y : STA $0375,Y ; sprite Y pos + STA !OAM_LOW+$1,Y : STA !OAM_LOW+$5,Y ; sprite Y pos LDA $1C : CLC : ADC $1A : SEC : SBC #$08 - STA $0379,Y : STA $037D,Y + STA !OAM_LOW+$9,Y : STA !OAM_LOW+$D,Y ; Sprite Attributes - xxxxxxxx yyyyyyyy YXPPpppt tttttttt ; x=X pos, y=Y pos, Y=Y flip, X=X flip ; P=Priority, p=Palette, t=Tile number %ai16() - LDA #$3A47 : STA $0372,Y ; %00111010 top-left - LDA #$7A47 : STA $0376,Y ; %01111010 top-right - LDA #$BA47 : STA $037A,Y ; %10111010 bottom-left - LDA #$FA47 : STA $037E,Y ; %11111010 bottom-right + LDA #$3A47 : STA !OAM_LOW+$2,Y ; %00111010 top-left + LDA #$7A47 : STA !OAM_LOW+$6,Y ; %01111010 top-right + LDA #$BA47 : STA !OAM_LOW+$A,Y ; %10111010 bottom-left + LDA #$FA47 : STA !OAM_LOW+$E,Y ; %11111010 bottom-right ; inc oam stack TYA : CLC : ADC #$0010 : STA !OAM_STACK_POINTER : TAY @@ -652,22 +652,22 @@ draw_enemyproj_hitbox: %a8() PLA ; X coord SEC : SBC $12 - STA $0370,Y + STA !OAM_LOW,Y CLC : ADC $12 : ADC $12 SEC : SBC #$08 - STA $0374,Y + STA !OAM_LOW+$4,Y PLA : PLA : DEC ; Y coord SEC : SBC $14 - STA $0371,Y + STA !OAM_LOW+$1,Y CLC : ADC $14 : ADC $14 SEC : SBC #$08 - STA $0375,Y + STA !OAM_LOW+$5,Y PLA %ai16() - LDA #$3A47 : STA $0372,Y ; %00111010 top-left - LDA #$FA47 : STA $0376,Y ; %11111010 bottom-right + LDA #$3A47 : STA !OAM_LOW+$2,Y ; %00111010 top-left + LDA #$FA47 : STA !OAM_LOW+$6,Y ; %11111010 bottom-right ; inc OAM stack ; vanilla routines use AND #$01FF to wrap the stack after 1FCh @@ -681,17 +681,17 @@ draw_enemyproj_hitbox: %a8() PLA ; X coord SEC : SBC $12 - STA $0370,Y : STA $0378,Y + STA !OAM_LOW,Y : STA !OAM_LOW+$8,Y CLC : ADC $12 : ADC $12 SEC : SBC #$08 - STA $0374,Y : STA $037C,Y + STA !OAM_LOW+$4,Y : STA !OAM_LOW+$C,Y PLA ; discard high byte ; Y coord PLA : DEC : SEC : SBC $14 - STA $0371,Y : STA $0375,Y + STA !OAM_LOW+$1,Y : STA !OAM_LOW+$5,Y CLC : ADC $14 : ADC $14 : SEC : SBC #$08 - STA $0379,Y : STA $037D,Y + STA !OAM_LOW+$9,Y : STA !OAM_LOW+$D,Y PLA ; discard high byte .setAttributes @@ -699,10 +699,10 @@ draw_enemyproj_hitbox: ; x=X pos, y=Y pos (low nibbles only), Y=Y flip, X=X flip ; P=Priority, p=Palette, t=Tile number %ai16() - LDA #$3A47 : STA $0372,Y ; %00111010 top-left - LDA #$7A47 : STA $0376,Y ; %01111010 top-right - LDA #$BA47 : STA $037A,Y ; %10111010 bottom-left - LDA #$FA47 : STA $037E,Y ; %11111010 bottom-right + LDA #$3A47 : STA !OAM_LOW+$2,Y ; %00111010 top-left + LDA #$7A47 : STA !OAM_LOW+$6,Y ; %01111010 top-right + LDA #$BA47 : STA !OAM_LOW+$A,Y ; %10111010 bottom-left + LDA #$FA47 : STA !OAM_LOW+$E,Y ; %11111010 bottom-right ; inc OAM stack ; vanilla routines use AND #$01FF to wrap the stack after 1FCh @@ -740,14 +740,14 @@ draw_enemyproj_hitbox: %a8() PLA ; left edge - STA $0370,Y : STA $0378,Y + STA !OAM_LOW,Y : STA !OAM_LOW+$8,Y CLC : ADC #$18 - STA $0374,Y : STA $037C,Y + STA !OAM_LOW+$4,Y : STA !OAM_LOW+$C,Y PLA : PLA ; top edge - STA $0371,Y : STA $0375,Y + STA !OAM_LOW+$1,Y : STA !OAM_LOW+$5,Y CLC : ADC #$18 - STA $0379,Y : STA $037D,Y + STA !OAM_LOW+$9,Y : STA !OAM_LOW+$D,Y PLA JMP .setAttributes @@ -788,16 +788,16 @@ draw_samusproj_hitbox: %a8() ; X coord PLA : SEC : SBC !SAMUS_PROJ_RADIUS_X,X - STA $0370,Y : STA $0378,Y ; X pos + STA !OAM_LOW,Y : STA !OAM_LOW+$8,Y ; X pos CLC : ADC !SAMUS_PROJ_RADIUS_X,X : ADC !SAMUS_PROJ_RADIUS_X,X : SEC : SBC #$08 - STA $0374,Y : STA $037C,Y + STA !OAM_LOW+$4,Y : STA !OAM_LOW+$C,Y PLA ; discard high byte ; Y coord PLA : DEC : SEC : SBC !SAMUS_PROJ_RADIUS_Y,X - STA $0371,Y : STA $0375,Y ; Y pos + STA !OAM_LOW+$1,Y : STA !OAM_LOW+$5,Y ; Y pos CLC : ADC !SAMUS_PROJ_RADIUS_Y,X : ADC !SAMUS_PROJ_RADIUS_Y,X : SEC : SBC #$08 - STA $0379,Y : STA $037D,Y + STA !OAM_LOW+$9,Y : STA !OAM_LOW+$D,Y PLA ; discard high byte .setAttributes @@ -805,10 +805,10 @@ draw_samusproj_hitbox: ; x=X pos, y=Y pos (low nibbles only), Y=Y flip, X=X flip ; P=Priority, p=Palette, t=Tile number %ai16() - LDA #$3A47 : STA $0372,Y ; %00111010 top-left - LDA #$7A47 : STA $0376,Y ; %01111010 top-right - LDA #$BA47 : STA $037A,Y ; %10111010 bottom-left - LDA #$FA47 : STA $037E,Y ; %11111010 bottom-right + LDA #$3A47 : STA !OAM_LOW+$2,Y ; %00111010 top-left + LDA #$7A47 : STA !OAM_LOW+$6,Y ; %01111010 top-right + LDA #$BA47 : STA !OAM_LOW+$A,Y ; %10111010 bottom-left + LDA #$FA47 : STA !OAM_LOW+$E,Y ; %11111010 bottom-right ; inc OAM stack ; vanilla routines use AND #$01FF to wrap the stack after 1FCh @@ -838,14 +838,14 @@ draw_samusproj_hitbox: %a8() PLA ; left edge - STA $0370,Y : STA $0378,Y + STA !OAM_LOW,Y : STA !OAM_LOW+$8,Y CLC : ADC #$18 - STA $0374,Y : STA $037C,Y + STA !OAM_LOW+$4,Y : STA !OAM_LOW+$C,Y PLA : PLA ; top edge - STA $0371,Y : STA $0375,Y + STA !OAM_LOW+$1,Y : STA !OAM_LOW+$5,Y CLC : ADC #$18 - STA $0379,Y : STA $037D,Y + STA !OAM_LOW+$9,Y : STA !OAM_LOW+$D,Y PLA JMP .setAttributes @@ -962,20 +962,20 @@ draw_custom_boss_hitbox: LDY !OAM_STACK_POINTER %a8() ; X coord - LDA $14 : STA $0370,Y : STA $0378,Y - LDA $18 : STA $0374,Y : STA $037C,Y + LDA $14 : STA !OAM_LOW,Y : STA !OAM_LOW+$8,Y + LDA $18 : STA !OAM_LOW+$4,Y : STA !OAM_LOW+$C,Y ; Y coord - LDA $16 : DEC : STA $0371,Y : STA $0375,Y - LDA $1A : STA $0379,Y : STA $037D,Y + LDA $16 : DEC : STA !OAM_LOW+$1,Y : STA !OAM_LOW+$5,Y + LDA $1A : STA !OAM_LOW+$9,Y : STA !OAM_LOW+$D,Y ; Sprite Attributes - xxxxxxxx yyyyyyyy YXPPpppt tttttttt ; x=X pos, y=Y pos (low nibbles only), Y=Y flip, X=X flip ; P=Priority, p=Palette, t=Tile number %ai16() - LDA #$3A47 : STA $0372,Y ; %00111010 top-left - LDA #$7A47 : STA $0376,Y ; %01111010 top-right - LDA #$BA47 : STA $037A,Y ; %10111010 bottom-left - LDA #$FA47 : STA $037E,Y ; %11111010 bottom-right + LDA #$3A47 : STA !OAM_LOW+$2,Y ; %00111010 top-left + LDA #$7A47 : STA !OAM_LOW+$6,Y ; %01111010 top-right + LDA #$BA47 : STA !OAM_LOW+$A,Y ; %10111010 bottom-left + LDA #$FA47 : STA !OAM_LOW+$E,Y ; %11111010 bottom-right ; inc oam stack TYA : CLC : ADC #$0010 : STA !OAM_STACK_POINTER @@ -1005,26 +1005,26 @@ draw_custom_boss_hitbox: %a8() ; X coord PLA : SEC : SBC #$0E - STA $0370,Y : STA $0378,Y ; X pos + STA !OAM_LOW,Y : STA !OAM_LOW+$8,Y ; X pos CLC : ADC #$14 - STA $0374,Y : STA $037C,Y + STA !OAM_LOW+$4,Y : STA !OAM_LOW+$C,Y PLA ; discard high byte ; Y coord PLA : DEC : SEC : SBC #$0E - STA $0371,Y : STA $0375,Y + STA !OAM_LOW+$1,Y : STA !OAM_LOW+$5,Y CLC : ADC #$14 - STA $0379,Y : STA $037D,Y + STA !OAM_LOW+$9,Y : STA !OAM_LOW+$D,Y PLA ; discard high byte ; Sprite Attributes - xxxxxxxx yyyyyyyy YXPPpppt tttttttt ; x=X pos, y=Y pos (low nibbles only), Y=Y flip, X=X flip ; P=Priority, p=Palette, t=Tile number %ai16() - LDA #$3A47 : STA $0372,Y ; %00111010 top-left - LDA #$7A47 : STA $0376,Y ; %01111010 top-right - LDA #$BA47 : STA $037A,Y ; %10111010 bottom-left - LDA #$FA47 : STA $037E,Y ; %11111010 bottom-right + LDA #$3A47 : STA !OAM_LOW+$2,Y ; %00111010 top-left + LDA #$7A47 : STA !OAM_LOW+$6,Y ; %01111010 top-right + LDA #$BA47 : STA !OAM_LOW+$A,Y ; %10111010 bottom-left + LDA #$FA47 : STA !OAM_LOW+$E,Y ; %11111010 bottom-right ; inc oam stack TYA : CLC : ADC #$0010 : STA !OAM_STACK_POINTER @@ -1040,24 +1040,24 @@ DrawMBHitbox: %a8() ; X coord CLC : ADC $14 - STA $0370,Y : STA $0378,Y ; sprite X pos + STA !OAM_LOW,Y : STA !OAM_LOW+$8,Y ; sprite X pos LDA $1D : CLC : ADC $18 : SEC : SBC #$08 - STA $0374,Y : STA $037C,Y + STA !OAM_LOW+$4,Y : STA !OAM_LOW+$C,Y ; Y coord LDA $1C : DEC : CLC : ADC $16 - STA $0371,Y : STA $0375,Y ; sprite Y pos + STA !OAM_LOW+$1,Y : STA !OAM_LOW+$5,Y ; sprite Y pos LDA $1C : CLC : ADC $1A : SEC : SBC #$08 - STA $0379,Y : STA $037D,Y + STA !OAM_LOW+$9,Y : STA !OAM_LOW+$D,Y ; Sprite Attributes - xxxxxxxx yyyyyyyy YXPPpppt tttttttt ; x=X pos, y=Y pos, Y=Y flip, X=X flip ; P=Priority, p=Palette, t=Tile number %ai16() - LDA #$3A47 : STA $0372,Y ; %00111010 top-left - LDA #$7A47 : STA $0376,Y ; %01111010 top-right - LDA #$BA47 : STA $037A,Y ; %10111010 bottom-left - LDA #$FA47 : STA $037E,Y ; %11111010 bottom-right + LDA #$3A47 : STA !OAM_LOW+$2,Y ; %00111010 top-left + LDA #$7A47 : STA !OAM_LOW+$6,Y ; %01111010 top-right + LDA #$BA47 : STA !OAM_LOW+$A,Y ; %10111010 bottom-left + LDA #$FA47 : STA !OAM_LOW+$E,Y ; %11111010 bottom-right ; inc oam stack TYA : CLC : ADC #$0010 : STA !OAM_STACK_POINTER : TAY From 82f666fbad4993d97dea16e62f39e0169c240003 Mon Sep 17 00:00:00 2001 From: idle Date: Thu, 9 Jul 2026 00:00:45 -0500 Subject: [PATCH 3/5] Add option to show extra OOB tiles where the X position wraps around --- src/defines.asm | 3 +- src/main.asm | 2 +- src/mainmenu.asm | 4 ++ src/spritefeat.asm | 146 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 151 insertions(+), 4 deletions(-) diff --git a/src/defines.asm b/src/defines.asm index 787c0649..a1dde8bb 100644 --- a/src/defines.asm +++ b/src/defines.asm @@ -1378,9 +1378,10 @@ endif !SPRITE_32x32_PROJ = #$0040 !SPRITE_OOB_WATCH = #$0080 !SPRITE_HUD_SPACETIME = #$0100 -!SPRITE_HUD_XRAY = #$0200 !SPRITE_HUD_SPACETIME_INVERTED = #$FEFF +!SPRITE_HUD_XRAY = #$0200 !SPRITE_HUD_XRAY_INVERTED = #$FDFF +!SPRITE_OOB_X_WRAP = #$0400 !UPDATE_TIMERS_CTRL_SHORTCUT_TYPE = $05 diff --git a/src/main.asm b/src/main.asm index 5cdd58c9..f304dfad 100644 --- a/src/main.asm +++ b/src/main.asm @@ -17,7 +17,7 @@ lorom !VERSION_MAJOR = 2 !VERSION_MINOR = 7 !VERSION_BUILD = 7 -!VERSION_REV = 0 +!VERSION_REV = 1 table ../resources/normal.tbl print "" diff --git a/src/mainmenu.asm b/src/mainmenu.asm index bd0c1d1c..543a0370 100644 --- a/src/mainmenu.asm +++ b/src/mainmenu.asm @@ -1837,6 +1837,7 @@ if !PRESERVE_WRAM endif dw #$FFFF dw #sprites_oob_viewer + dw #sprites_oob_show_x_wrap dw #$0000 %cm_header("SPRITE FEATURES") @@ -1904,6 +1905,9 @@ sprites_oob_viewer: .skip_oob RTL +sprites_oob_show_x_wrap: + %cm_toggle_bit("OoB Show X Wraparound", !ram_sprite_feature_flags, !SPRITE_OOB_X_WRAP, #0) + ; -------------- ; Infohud menu diff --git a/src/spritefeat.asm b/src/spritefeat.asm index 9e42e18c..927d3462 100644 --- a/src/spritefeat.asm +++ b/src/spritefeat.asm @@ -126,7 +126,7 @@ draw_sprite_oob: LDA $16 : STA $4202 TYA : CLC : ADC $24 : STA $4203 NOP #2 ; wait for CPU math - %ai16() + %a16() ; room_width_blocks * (Y + [Samus Y - (oob_height*8)] / 16) LDA $4216 : STA $18 @@ -234,6 +234,11 @@ draw_sprite_oob: JMP .loop_y .end_y + LDA !ram_sprite_feature_flags : BIT !SPRITE_OOB_X_WRAP : BEQ .copy_stack + LDA $12 : BPL .copy_stack + CLC : ADC #((!oob_width-1)*16) : BPL draw_sprite_x_wrap + + .copy_stack LDA !OAM_STACK_POINTER : BEQ .end LSR #4 : INC : STA $CB LDA $C9 : LSR #4 : STA $C9 @@ -251,11 +256,148 @@ draw_sprite_oob: ; d: sx for sprite 4n+3 LDA #%10101010 : STA !OAM_HIGH,X INX : CPX $CB : BNE .copy_loop - %ai16() + %a16() .end JSR draw_oob_samus_hitbox RTS +} + +draw_sprite_x_wrap: +{ + ; Find the index where position goes from negative to positive + LDX #$0000 + LDA $12 + .search + INX : CLC : ADC #$0010 : BMI .search + + ; Collision checks oscillate between left-to-right and right-to-left + ; Either way, the game looks up a block index and adds/subtracts from there + ; Normally there isn't much difference between the two checks, + ; but in the case where X position overflows or underflows, + ; there is a significant difference between left-to-right and right-to-left. + ; The end result is the game can check double the tiles it normally would, + ; but it only checks half of them each frame. + ; + ; To represent this, we want to draw the pairs of extra tiles along the Y-axis, + ; but each frame we will alternate which of the two extra tiles to draw. + ; This is done for efficiency and also to illustrate the oscillating effect. + STX $C1 : LDY #$0000 + LDA $C5 : CLC : ADC #$0008 : STA $C5 + LDA !FRAME_COUNTER : BIT #$0001 : BNE .loop + DEC $C1 + LDA $C5 : SEC : SBC #$000F : STA $C5 + + .loop + %a8() + LDA $16 : STA $4202 + TYA : CLC : ADC $24 : STA $4203 + NOP #2 ; wait for CPU math + %a16() + ; room_width_blocks * (Y + [Samus Y - (oob_height*8)] / 16) + LDA $4216 : STA $18 + + PHY + STY $C3 + + ; X + [Samus X - (oob_width*8)] / 16 + LDA $C1 : CLC : ADC $22 : AND #$0FFF + ORA #$1000 : BIT #$0800 : BEQ .continue + ORA #$F000 + + .continue + ; a = (width * bit.lrshift(bit.band(cameraY+y*16, 0xFFF), 4)) + bit.lrshift(bit.band(cameraX+x*16, 0xFFFF), 4) + CLC : ADC $18 + ; a = a * 2 + ASL : TAX + ; Load clipdata of block + LDA !LEVEL_DATA+1,X : AND #$00F0 + CMP #$00D0 : BEQ .vertical : CMP #$0050 : BNE .get_sprite_id + + .horizontal + ; Horizontal extension, try once to resolve it + TXA : LSR : TAX + LDA !LEVEL_BTS,X : AND #$00FF : BEQ .next + BIT #$0080 : BEQ .horizontal_addition + ORA #$FF00 + .horizontal_addition + PHX : CLC : ADC 1,S : PLX + BRA .extension_block + + .vertical + ; Vertical extension, try once to resolve it + TXA : LSR : TAX + LDA !LEVEL_BTS,X : AND #$00FF : BEQ .next + PHX + BIT #$0080 : BNE .negative_vertical + TAX + PLA + .loop_positive_vertical + CLC : ADC !ROOM_WIDTH_BLOCKS + DEX : BNE .loop_positive_vertical + + .extension_block + ASL : TAX + LDA !LEVEL_DATA+1,X : AND #$00F0 + + .get_sprite_id + ; Get sprite ID for this block type + CMP #$0030 : BEQ .check_scroll + LSR #3 : TAX + .get_block_gfx + LDA.l block_gfx,X : BEQ .next + + ; Set sprite ID + %a8() + LDY !OAM_STACK_POINTER : STA !OAM_LOW+$2,Y + + ; Get X coord + LDA $C1 : INC #2 : ASL #4 : SEC : SBC $C5 + STA !OAM_LOW,Y + + ; Get Y coord + LDA $C3 : CLC : ADC #$04 : ASL #4 : SEC : SBC $C7 + STA !OAM_LOW+$1,Y + + ; Sprite Attributes - xxxxxxxx yyyyyyyy YXPPpppt tttttttt + ; x=X pos, y=Y pos (low nibbles only), Y=Y flip, X=X flip + ; P=Priority, p=Palette, t=Tile number + ; Priority bits set, palette = 101 + LDA #%00111010 : STA !OAM_LOW+$3,Y + + %a16() + + INY #4 + STY !OAM_STACK_POINTER + + .next + PLY : INY : CPY #!oob_height : BEQ .end + JMP .loop + + .end + JMP draw_sprite_oob_copy_stack + + .negative_vertical + ORA #$FF00 : TAX + PLA + .loop_negative_vertical + SEC : SBC !ROOM_WIDTH_BLOCKS + INX : BNE .loop_negative_vertical + + ; For negative vertical extension into horizontal extension, + ; allow one more attempt (handles 2x2 blocks) + ASL : TAX + LDA !LEVEL_DATA+1,X : AND #$00F0 + CMP #$0050 : BNE .get_sprite_id + JMP .horizontal + + .check_scroll + TXA : LSR : TAX + LDA !LEVEL_BTS,X : AND #$00FF + CMP #$0046 : BEQ .next + LDX #$0006 + BRA .get_block_gfx +} block_gfx: ; 00 = transparent From 5c5cdcc023f16b1e3bccc3e22b30d4aa764c8f12 Mon Sep 17 00:00:00 2001 From: idle Date: Thu, 9 Jul 2026 01:02:51 -0500 Subject: [PATCH 4/5] Add 0% addresses to RAM watch menu --- src/defines.asm | 3 ++- src/ramwatchmenu.asm | 47 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/defines.asm b/src/defines.asm index a1dde8bb..21666aaf 100644 --- a/src/defines.asm +++ b/src/defines.asm @@ -1171,9 +1171,10 @@ !MAP_TILES_EXPLORED_CERES = $7ED352 !MAP_TILES_EXPLORED_DEBUG = $7ED452 !EVENT_BIT_ARRAY = $7ED820 -!MAP_DATA_EXPLORED_COMPRESSED = $7ED91C +!OPENED_DOOR_BIT_ARRAY = $7ED8B0 !MAP_STATION_FLAGS = $7ED908 !LOADING_GAME_STATE = $7ED914 +!MAP_DATA_EXPLORED_COMPRESSED = $7ED91C !INPUT_BIND_UP = $7E09AA !INPUT_BIND_DOWN = $7E09AC diff --git a/src/ramwatchmenu.asm b/src/ramwatchmenu.asm index 2ed813c8..6e5d1c3a 100644 --- a/src/ramwatchmenu.asm +++ b/src/ramwatchmenu.asm @@ -60,6 +60,7 @@ RAMWatchCommonMenu: dw ramwatch_common_proj dw ramwatch_common_misc dw ramwatch_common_anyglitched + dw ramwatch_common_zeropercent dw #$0000 %cm_header("CHOOSE RAM CATEGORY") @@ -82,7 +83,10 @@ ramwatch_common_misc: %cm_submenu("Misc Addresses", #RAMWatchCommonMiscMenu) ramwatch_common_anyglitched: - %cm_submenu("Any% Glitched", #RAMWatchCommonAnyGlitchedMenu) + %cm_submenu("Any% Glitched Addresses", #RAMWatchCommonAnyGlitchedMenu) + +ramwatch_common_zeropercent: + %cm_submenu("0% Addresses", #RAMWatchCommonZeroPercentMenu) RAMWatchCommonEnemyMenu: dw ramwatch_common_enemy_side @@ -596,6 +600,47 @@ ramwatch_common_anyglitched_1D5B: ramwatch_common_anyglitched_D820: %cm_jsl("D820 Zebes Ablaze F---", action_select_common_address, #!EVENT_BIT_ARRAY) +RAMWatchCommonZeroPercentMenu: + dw ramwatch_common_zeropercent_09A2 + dw ramwatch_common_zeropercent_09A4 + dw ramwatch_common_zeropercent_09C8 + dw ramwatch_common_zeropercent_D820 + dw ramwatch_common_zeropercent_D82E + dw ramwatch_common_zeropercent_D8B0 + dw ramwatch_common_zeropercent_D914 + dw ramwatch_common_zeropercent_78F6 + dw ramwatch_common_zeropercent_92F2 + dw #$0000 + %cm_header("SELECT 0% RAM") + %cm_footer("DESIRED VALUE ON RIGHT") + +ramwatch_common_zeropercent_09A2: + %cm_jsl(" 09A2 Equipped Items FFFF", action_select_common_address, #!SAMUS_ITEMS_EQUIPPED) + +ramwatch_common_zeropercent_09A4: + %cm_jsl(" 09A4 Collected Items 0000", action_select_common_address, #!SAMUS_ITEMS_COLLECTED) + +ramwatch_common_zeropercent_09C8: + %cm_jsl(" 09C8 Max Missiles 0000", action_select_common_address, #!SAMUS_MISSILES_MAX) + +ramwatch_common_zeropercent_D820: + %cm_jsl(" D820 Zebes Ablaze F---", action_select_common_address, #!EVENT_BIT_ARRAY) + +ramwatch_common_zeropercent_D82E: + %cm_jsl(" D82E Ceres Ridley ---0", action_select_common_address, #(!EVENT_BIT_ARRAY+$E)) + +ramwatch_common_zeropercent_D8B0: + %cm_jsl(" D8B0 Opened Door F---", action_select_common_address, #!OPENED_DOOR_BIT_ARRAY) + +ramwatch_common_zeropercent_D914: + %cm_jsl(" D914 Load Game State 0000", action_select_common_address, #!LOADING_GAME_STATE) + +ramwatch_common_zeropercent_78F6: + %cm_jsl("178F6 5D Block 5D--", action_select_common_address, #$7F78F6) + +ramwatch_common_zeropercent_92F2: + %cm_jsl("192F2 Exit Block --81", action_select_common_address, #$7F92F2) + action_select_common_address: { TYA : STA !ram_cm_watch_common_address From 15626ccae43eaeb7b96a8e1320277343356cc3b3 Mon Sep 17 00:00:00 2001 From: idle Date: Thu, 9 Jul 2026 23:15:43 -0500 Subject: [PATCH 5/5] Release 2.7.7.1 --- src/defines.asm | 1 + src/ramwatchmenu.asm | 4 ++++ web/data/changelog.mdx | 2 ++ web/data/config.json | 2 +- web/data/help.mdx | 6 ++++-- 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/defines.asm b/src/defines.asm index 21666aaf..2d360864 100644 --- a/src/defines.asm +++ b/src/defines.asm @@ -908,6 +908,7 @@ !DEMO_PREINSTRUCTION_POINTER = $0A7A !DEMO_INSTRUCTION_TIMER = $0A7C !DEMO_INSTRUCTION_POINTER = $0A7E +!XRAY_ANGLE = $0A82 !DEMO_CONTROLLER_PRI = $0A84 !DEMO_INPUT_ENABLED = $0A88 !DEMO_PREVIOUS_CONTROLLER_PRI = $0A8C diff --git a/src/ramwatchmenu.asm b/src/ramwatchmenu.asm index 6e5d1c3a..f4ca25dc 100644 --- a/src/ramwatchmenu.asm +++ b/src/ramwatchmenu.asm @@ -492,6 +492,7 @@ RAMWatchCommonMiscMenu: dw ramwatch_common_misc_09DC dw ramwatch_common_misc_09DE dw ramwatch_common_misc_09E0 + dw ramwatch_common_misc_0A82 dw ramwatch_common_misc_0DC4 dw ramwatch_common_misc_0E50 dw ramwatch_common_misc_0E52 @@ -523,6 +524,9 @@ ramwatch_common_misc_09DE: ramwatch_common_misc_09E0: %cm_jsl("Game Time, Hours", action_select_common_address, #!IGT_HOURS) +ramwatch_common_misc_0A82: + %cm_jsl("X-Ray Angle", action_select_common_address, #!XRAY_ANGLE) + ramwatch_common_misc_0DC4: %cm_jsl("Current Block Index", action_select_common_address, #!CURRENT_BLOCK_INDEX) diff --git a/web/data/changelog.mdx b/web/data/changelog.mdx index 7b6c1e51..2e7a1a80 100644 --- a/web/data/changelog.mdx +++ b/web/data/changelog.mdx @@ -55,6 +55,8 @@ - Move RNG control to SRAM, with option defaulted on to still reset it when SNES hard resets (2.7.7) - Fine-tune door transition lag and make minimap toggle easier to use (2.7.7) - Prevent shinetune from showing L# when dash pressed near beginning of movement (2.7.7) +- Fix crash when OOB x-ray aiming up, and add 0% common RAM watch addresses (2.7.7.1) +- Add OOB show X wraparound option (2.7.7.1) # Version 2.6.x - Optimize kraid rock projectiles to reduce lag when Kraid rises (2.6.0) diff --git a/web/data/config.json b/web/data/config.json index 7771d43e..2ae1165e 100644 --- a/web/data/config.json +++ b/web/data/config.json @@ -1,6 +1,6 @@ { "name": "Super Metroid Practice Hack", - "version": "2.7.7", + "version": "2.7.7.1", "variants": ["NTSC", "PAL"], "base": { "NTSC": { diff --git a/web/data/help.mdx b/web/data/help.mdx index 874b25f2..e149d862 100644 --- a/web/data/help.mdx +++ b/web/data/help.mdx @@ -131,7 +131,6 @@ If you experience crashes or severe glitches when using savestates, your platfor | | Any or all of the 12 controller inputs can be individually toggled into frames held mode. | - Status Icons | Toggle icons to appear when standing on an elevator or when the shine timer is active (e.g. while carrying a spikesuit). | - Bonk Indicators | Toggles the two B indicators that appear on the InfoHUD when Samus bonks a wall and it prevents her from taking an action such as jumping. -| - Spacetime HUD | Option to preserve the InfoHUD when using spacetime beam | - Customize RAM Watch | Setup addresses to be monitored by the RAM Watch HUD mode. Address 1 will appear in the bottom-left corner of the HUD, while Address 2 appears just to the right of Address 1. Memory at these locations can be edited by configuring a Value and selecting 'Write to Address'. Toggle 'Lock Value' to automatically write the value once every frame. RAM Watch must must be the selected HUD mode to continue writing to the address. Only one byte of hex can be configured at a time in the menu. 'Hi' refers to the first byte, while 'Lo' refers to the second. Set the Hi Address to 0D and the Lo Address to EC to monitor the memory address $0DEC, which stores the counter for Samus direction changes while held by Draygon (among other uses). You can find lists of known RAM addresses by selecting 'Select Common Addresses'. | **Timer Settings** | Located within the InfoHUD menu | - Frame Counters | Toggle the room timer in the top-right of the HUD between realtime, gametime, and "speedrun time". @@ -150,7 +149,10 @@ If you experience crashes or severe glitches when using savestates, your platfor | - Samus Projectile Hitbox | Toggles an overlay of Samus projectile hitboxes that are drawn on top of Samus projectile sprites. | - Enemy Projectile Hitbox | Toggles an overlay of enemy projectile hitboxes that are drawn on top of enemy projectile sprites. | - 32x32 Projectile Boxes | Toggles an overlay of the 32x32 grid used for projectile vs projectile collisions. Requires either Samus or Enemy Projectile Hitboxes to be active. -| - OOB Tile Viewer | This enables an overlay that shows the tiles near Samus at all times, disconnected from the camera. This allows you to navigate OOB (Out of Bounds) areas where neither Samus nor the actual tiles are visible in-game. Samus hitbox is drawn at the center of the overlay to show the position relative to the tiles. +| - Spacetime HUD | Option to preserve the InfoHUD when using spacetime beam +| - X-Ray HUD | Option to preserve the InfoHUD when using x-ray out of bounds +| - OoB Tile Viewer | This enables an overlay that shows the tiles near Samus at all times, disconnected from the camera. This allows you to navigate OOB (Out of Bounds) areas where neither Samus nor the actual tiles are visible in-game. Samus hitbox is drawn at the center of the overlay to show the position relative to the tiles. +| - OoB Show X Wraparound | When OoB tile viewer enabled, this shows an extra set of tiles that Samus can interact with along the Y axis where X can jump from 0 to -1 (FFFF), which the game treats as a large number for collision purposes. | | | **Room Layout** | Modify the room layout, usually to help practice rando scenarios. | - Item Pickups | Option to replace all items with a specific item. Can also be done by icon type (visible, chozo orb, hidden).