;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Dynamic Levels Patch ;; ;; Version 1.0 Final ;; ;; ;; ;; By Vitor Vilela ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; header lorom ; $00BA4D - $00BA5F is unused on original (FFs). ; $00BA60 - $00BA7F is unused for this patch. ; $00BA9C - $00BABB is unused for this patch. ; $00BB38 - $00BBEB is unused for this patch. ; $00BCA0 - $00BD53 is unused for this patch. ; TO DO: Item Memory Tracking probably won't work correctly in the other screen boundaries. ; Recalculating the offset is a good way to go. ; TO DO: SA-1 remap support. ; TO DO for making it actually fully functional: ; 1) Add "YYYY" to "Screen Jump" object for allow accessing the ; additional "vertical screens" along the "normal screens" ; ; 000PPPPP 0000---- 00000001 ; ; Where PPPPP is the new screen number and ---- the proposal for storing the ; additional vertical screens (or just the high values of "yyyy" of every object). ; ; 2) Make it possible to set initial Mario position and FG/BG positions to the ; expanded level area. ; ; 3) Add compatibility with large objects, if that's an interesting thing. ; ; 4) Adjust any other additional LM ASM hack that accesses the map16 manually, as an ; custom object. Pretty much involves updating either the table or replacing #$01B0 ; references to !Screen_Size. ; Theoretically, up to 64 screens are possible if we use 768 bytes of free buffer RAM ; instead of 384. But it would cost managing more data and it's not very the kind of ; the purpose aims for. ; $13D7 is originally only used on OW. ; $0AF5 is cleared on boss clear but it should not matter. !Screen_Size = $13D7 ; Screen size (so it defaults to e.g. #$01B0) ; Slight risk to this free RAM address being used by another third party ASM hack. ; Probably would be better to edit smkdan's patch and use part of the reserved RAM addresses ; for it or even adapt to use scratch RAM ($00-$0F) for better speed. ADC #$0000 is 3 cycles. ; ADC dp is 4 cycles. ADC addr is 5 cycles. ADC long is 6 cycles. !Screen_Size_L = $1936 ; Screen size - #$0010. Used to adjust smkdan's patch. !ExSprite_Flags = $0BF4 ; Holds the currently loaded flags for exsprites !ExLevel_Flags = $0BF5 ; Holds the currently loaded flags for the level. ; This RAM buffer is originally only used on OW and boss battles. ; Note that part of $0AF6 is used by LM to include the extended color area ; that the original game didn't include. That makes our hack also uses ; part of $0BF6, which makes mandatory to use the 4BPP hack or some additional hijacks ; to clean up it... ; Starting at $0B05 is free! ; IMPORTANT: Skipped first 256 bytes because the RAM area is used by credits. ; Sticking with $0BF6 and forward for tables and backward for RAM values. !RAM = $0BF6 ; 256 bytes of free RAM. Must be on shadow RAM. !L1_Screen_Lo = !RAM ; 96 bytes (32 * 3). !L2_Screen_Lo = !RAM+48 ; 48 bytes (shared). !L1_Screen_Hi = !RAM+96 ; 96 bytes (32 * 3). !L2_Screen_Hi = !RAM+96+48 ; 48 bytes (shared). ; These ones don't need to be on the shadow RAM, though... !L1_Lookup_Lo = !RAM+96+96 ; 32 bytes !L2_Lookup_Lo = !RAM+96+96+16 ; 16 bytes (shared) !L1_Lookup_Hi = !RAM+96+96+32 ; 32 bytes !L2_Lookup_Hi = !RAM+96+96+32+16 ; 16 bytes (shared) ; Adjust smkdan's patch ; C2 20 A5 1C 29 F0 01 48 ;;prepare initial index based on both existing column select and row select ; REP #$20 ; LDA !L1yScroll ;yscroll /16 = row ; AND #$01F0 ;column select is bits 0-3, row select is bits 4-8 ; PHA ; change to AND #$3FF0. assert read4($1FAAC3) == $1CA520C2, "smkdan patch is different than expected." org $1FAAC3+4 AND #$3FF0 ; C2 20 A5 20 29 F0 01 48 ;;prepare initial index based on both existing column select and row select ; REP #$20 ; LDA !L2yScroll ;yscroll /16 = row ; AND #$01F0 ;column select is bits 0-3, row select is bits 4-8 ; PHA assert read4($1FAB5A) == $20A520C2, "smkdan patch is different than expected." org $1FAB5A+4 AND #$3FF0 ; This makes the "+1 screen" on horizontal L1/L2 writer ; use the dynamic screen size instead of constant #$01B0 - #$0010 macro smkd_offset1(pos) assert read2($1F8044+(*$42)) == $188A, "smkdan patch is different than expected." org $1F8044+(*$42)+2 ADC.w !Screen_Size_L endmacro macro smkd_offset2(pos) assert read2($1F8906+(*$4A)) == $188A, "smkdan patch is different than expected." org $1F8906+(*$4A)+2 ADC.w !Screen_Size_L endmacro ; This one replaces the AND #$01FF to AND #$3FFF ; because the patch thinks the scroll value is negatively ; while in fact it's actually correct. AND #$3FFF is the maximum possible ; because of the map16 size ($3800 bytes). macro smkd_offset3(pos) ;assert read2($1F8561+(*$39)) == $188A, "smkdan patch is different than expected." org $1F8561+(*$39)+5 AND.w #$3FFF endmacro macro smkd_offset4(pos) ;assert read2($1F8EC3+(*$41)) == $188A, "smkdan patch is different than expected." org $1F8EC3+(*$41)+5 AND.w #$3FFF endmacro %smkd_offset1($00) ; $14 TIMES %smkd_offset1($01) %smkd_offset1($02) %smkd_offset1($03) %smkd_offset1($04) %smkd_offset1($05) %smkd_offset1($06) %smkd_offset1($07) %smkd_offset1($08) %smkd_offset1($09) %smkd_offset1($0A) %smkd_offset1($0B) %smkd_offset1($0C) %smkd_offset1($0D) %smkd_offset1($0E) %smkd_offset1($0F) %smkd_offset1($10) %smkd_offset1($11) %smkd_offset1($12) %smkd_offset1($13) %smkd_offset2($00) ; $14 TIMES %smkd_offset2($01) %smkd_offset2($02) %smkd_offset2($03) %smkd_offset2($04) %smkd_offset2($05) %smkd_offset2($06) %smkd_offset2($07) %smkd_offset2($08) %smkd_offset2($09) %smkd_offset2($0A) %smkd_offset2($0B) %smkd_offset2($0C) %smkd_offset2($0D) %smkd_offset2($0E) %smkd_offset2($0F) %smkd_offset2($10) %smkd_offset2($11) %smkd_offset2($12) %smkd_offset2($13) %smkd_offset3($00) ; $10 times %smkd_offset3($01) %smkd_offset3($02) %smkd_offset3($03) %smkd_offset3($04) %smkd_offset3($05) %smkd_offset3($06) %smkd_offset3($07) %smkd_offset3($08) %smkd_offset3($09) %smkd_offset3($0A) %smkd_offset3($0B) %smkd_offset3($0C) %smkd_offset3($0D) %smkd_offset3($0E) %smkd_offset3($0F) %smkd_offset4($00) ; $10 times %smkd_offset4($01) %smkd_offset4($02) %smkd_offset4($03) %smkd_offset4($04) %smkd_offset4($05) %smkd_offset4($06) %smkd_offset4($07) %smkd_offset4($08) %smkd_offset4($09) %smkd_offset4($0A) %smkd_offset4($0B) %smkd_offset4($0C) %smkd_offset4($0D) %smkd_offset4($0E) %smkd_offset4($0F) ;DATA_00BA60: .db $00,$B0,$60,$10,$C0,$70,$20,$D0 ;CODE_00F492: 7F 60 BA 00 ADC.L DATA_00BA60,X org $00F492 ADC.L !L1_Lookup_Lo,x ;CODE_019500: BF 60 BA 00 LDA.L DATA_00BA60,X org $019500 LDA.L !L1_Lookup_Lo,x ;CODE_01D97B: 7F 60 BA 00 ADC.L DATA_00BA60,X org $01D97B ADC.L !L1_Lookup_Lo,x ;CODE_0292F9: BF 60 BA 00 LDA.L DATA_00BA60,X org $0292F9 LDA.L !L1_Lookup_Lo,x ;CODE_0295EC: BF 60 BA 00 LDA.L DATA_00BA60,X org $0295EC LDA.L !L1_Lookup_Lo,x ;CODE_02A6BA: BF 60 BA 00 LDA.L DATA_00BA60,X org $02A6BA LDA.L !L1_Lookup_Lo,x ;CODE_02BA71: BF 60 BA 00 LDA.L DATA_00BA60,X org $02BA71 LDA.L !L1_Lookup_Lo,x ;CODE_02D18C: BF 60 BA 00 LDA.L DATA_00BA60,X org $02D18C LDA.L !L1_Lookup_Lo,x ;DATA_00BA70: .db $00,$B0,$60,$10,$C0,$70,$20,$D0 ;CODE_00F50D: 7F 70 BA 00 ADC.L DATA_00BA70,X org $00F50D ADC.L !L2_Lookup_Lo,x ;CODE_019509: BF 70 BA 00 LDA.L DATA_00BA70,X org $019509 LDA.L !L2_Lookup_Lo,x ;CODE_029301: BF 70 BA 00 LDA.L DATA_00BA70,X org $029301 LDA.L !L2_Lookup_Lo,x ;CODE_0295F4: BF 70 BA 00 LDA.L DATA_00BA70,X org $0295F4 LDA.L !L2_Lookup_Lo,x ;CODE_02A6C2: BF 70 BA 00 LDA.L DATA_00BA70,X org $02A6C2 LDA.L !L2_Lookup_Lo,x ;ADDR_02BA79: BF 70 BA 00 LDA.L DATA_00BA70,X org $02BA79 LDA.L !L2_Lookup_Lo,x ;ADDR_02D194: BF 70 BA 00 LDA.L DATA_00BA70,X org $02D194 LDA.L !L2_Lookup_Lo,x ;DATA_00BA9C: .db $C8,$C9,$CB,$CD,$CE,$D0,$D2,$D3 ;CODE_00F49A: 7F 9C BA 00 ADC.L DATA_00BA9C,X org $00F49A ADC.L !L1_Lookup_Hi,x ;CODE_019512: BF 9C BA 00 LDA.L DATA_00BA9C,X org $019512 LDA.L !L1_Lookup_Hi,x ;CODE_01D981: BF 9C BA 00 LDA.L DATA_00BA9C,X org $01D981 LDA.L !L1_Lookup_Hi,x ;CODE_02930A: BF 9C BA 00 LDA.L DATA_00BA9C,X org $02930A LDA.L !L1_Lookup_Hi,x ;CODE_0295FD: BF 9C BA 00 LDA.L DATA_00BA9C,X org $0295FD LDA.L !L1_Lookup_Hi,x ;CODE_02A6CB: BF 9C BA 00 LDA.L DATA_00BA9C,X org $02A6CB LDA.L !L1_Lookup_Hi,x ;CODE_02BA82: BF 9C BA 00 LDA.L DATA_00BA9C,X org $02BA82 LDA.L !L1_Lookup_Hi,x ;CODE_02D19D: BF 9C BA 00 LDA.L DATA_00BA9C,X org $02D19D LDA.L !L1_Lookup_Hi,x ;DATA_00BAAC: .db $E3,$E4,$E6,$E8,$E9,$EB,$ED,$EE ;CODE_00F515: 7F AC BA 00 ADC.L DATA_00BAAC,X org $00F515 ADC.L !L2_Lookup_Hi,x ;CODE_01951B: BF AC BA 00 LDA.L DATA_00BAAC,X org $01951B LDA.L !L2_Lookup_Hi,x ;CODE_029312: BF AC BA 00 LDA.L DATA_00BAAC,X org $029312 LDA.L !L2_Lookup_Hi,x ;CODE_029605: BF AC BA 00 LDA.L DATA_00BAAC,X org $029605 LDA.L !L2_Lookup_Hi,x ;CODE_02A6D3: BF AC BA 00 LDA.L DATA_00BAAC,X org $02A6D3 LDA.L !L2_Lookup_Hi,x ;ADDR_02BA8A: BF AC BA 00 LDA.L DATA_00BAAC,X org $02BA8A LDA.L !L2_Lookup_Hi,x ;ADDR_02D1A5: BF AC BA 00 LDA.L DATA_00BAAC,X org $02D1A5 LDA.L !L2_Lookup_Hi,x ; TODO: do something with Mario initial position ; do something with FG/BG initial position ; see: CODE_05D975 ; CODE_00F478: C9 B0 01 CMP.W #$01B0 org $00F478 CMP.w !Screen_Size ; CODE_00F4F3: C9 B0 01 CMP.W #$01B0 org $00F4F3 CMP.w !Screen_Size ; Sprite interaction probably. ; CODE_0194D6: C9 B0 01 CMP.W #$01B0 org $0194D6 CMP.w !Screen_Size ; CODE_03D793: 69 B0 01 ADC.W #$01B0 org $03D793 ADC.w !Screen_Size ; Not gonna modify: actually is part of the level clearing routine. ; has nothing to do. But it would be nice to clear the entire area? ; regardless, this is OW stuff. ; CODE_04DC33: E0 B0 01 CPX.W #$01B0 ; This CMP might suggests that smkdan's vram patch is required for ; working, because the tilemap on the VRAM is 512x512 and probably ; it's not updated vertically on horizontal levels. ; CODE_058A94: C9 B0 01 CMP.W #$01B0 ; Same goes for this one. I'm not modifying these because it will ; likely end up corrupting the map16 upload table instead of ; making the levels larger (note: this is for layer 2) ; CODE_058C6A: C9 B0 01 CMP.W #$01B0 ; highly suggestive to be OW. ; CODE_058DB1: 69 B0 01 ADC.W #$01B0 ; same ; CODE_058DB9: 69 B0 01 ADC.W #$01B0 ; same ; CODE_058E12: C9 B0 01 CMP.W #$01B0 ; a somewhat weird structure, for copying from c800 to c800 + screen size ; maybe related to no yoshi intro? ; CODE_0DE028: A9 B0 01 LDA.W #$01B0 ;\ ; would be good to investigate. ; CODE_0DE03D: A9 B0 01 LDA.W #$01B0 ;\ ; CODE_0DA963: A5 6B LDA $6B ;\ ; CODE_0DA965: 18 CLC ; | ; CODE_0DA966: 69 B0 ADC.B #$B0 ; | ; CODE_0DA968: 85 6B STA $6B ; | ; CODE_0DA96A: 85 6E STA $6E ; | Increase pointer ; CODE_0DA96C: A5 6C LDA $6C ; | ; CODE_0DA96E: 69 01 ADC.B #$01 ; | ; CODE_0DA970: 85 6C STA $6C ; | ; CODE_0DA972: 85 6F STA $6F ;/ ; CODE_0DA974: EE A1 1B INC.W $1BA1 ; Increase screen number org $0DA963 REP #$21 LDA $6B ADC.w !Screen_Size STA $6B STA $6E LDA #$0000 SEP #$20 NOP warnpc $0DA974 ;CODE_0DA9D6: ; LDA $6B ; SEC ;$0DA9D8 | ; SBC.b #$B0 ;$0DA9D9 | ; STA $6B ;$0DA9DB | ; STA $6E ;$0DA9DD | ; STA $04 ;$0DA9DF | ; LDA $6C ;$0DA9E1 | ; SBC.b #$01 ;$0DA9E3 | ; STA $6C ;$0DA9E5 | ; STA $6F ;$0DA9E7 | ; STA $05 ;$0DA9E9 | ; DEC.w $1BA1 ;$0DA9EB | ; RTS ;$0DA9EE | org $0DA9D6 REP #$20 LDA $6B SEC SBC.w !Screen_Size STA $6B STA $6E STA $04 LDA #$0000 SEP #$20 DEC $1BA1 RTS NOP #2 warnpc $0DA9EF ;CODE_0DA9EF: ; LDA $6B ; CLC ;$0DA9F1 | ; ADC.b #$B0 ;$0DA9F2 | ; STA $6B ;$0DA9F4 | ; STA $6E ;$0DA9F6 | ; STA $04 ;$0DA9F8 | ; LDA $6C ;$0DA9FA | ; ADC.b #$01 ;$0DA9FC | ; STA $6C ;$0DA9FE | ; STA $6F ;$0DAA00 | ; STA $05 ;$0DAA02 | ; INC.w $1BA1 ;$0DAA04 | ; RTS ;$0DAA07 | org $0DA9EF REP #$21 LDA $6B ADC.w !Screen_Size STA $6B STA $6E STA $04 LDA #$0000 SEP #$20 INC $1BA1 RTS NOP #3 warnpc $0DAA08 ;CODE_0DBB16: A5 6B LDA $6B ;\ ;CODE_0DBB18: 18 CLC ; | ;CODE_0DBB19: 69 B0 ADC.B #$B0 ; | ;CODE_0DBB1B: 85 6B STA $6B ; | ;CODE_0DBB1D: 85 6E STA $6E ; | Move pointer downwards one screen (if carry doesn't get set, it screws up) ;CODE_0DBB1F: A5 6C LDA $6C ; | ;CODE_0DBB21: 69 00 ADC.B #$00 ; | ;CODE_0DBB23: 85 6C STA $6C ; | ;CODE_0DBB25: 85 6F STA $6F ;/ ; what? ... not editing this for now. ; I suppose this is a mistake on the original game? I will ; let you decide if it's good or change this. ; Main hijack ; The hijack spot is complicate because while I do have access ; to the level number here, I don't have access if it's a layer 2 ; level or not. So I have to either either in one or another... ; I ended up picking level number and leave a flag for layer 2 ; level on the table. However it's not required for regular layer 2 ; levels with standard size, so it's not a big of deal. ; CODE_05D9A1: A5 5B LDA RAM_IsVerticalLvl ; CODE_05D9A3: 29 01 AND.B #$01 org $05D9A1 autoclean JSL screen_load_hack ; This adjusts the max vertical scroll for the levels. org $00F70D JML max_scroll NOP #2 ; this code won't be executed anyways. ; CODE_02950B: 64 0F STZ $0F ; CODE_02950D: 20 40 95 JSR.W CODE_029540 ; There is no custom cape interaction during the boss battles. ; Because of that, it threats like a regular level and because ; the lookup table is used during iggy/larry battles, random ; blocks are spawned when using the cape. org $02950B JML iggy_larry NOP ; Sprite memory used to be between $00 - $3F, but ; there is table only for up to $12. Change AND to #$1F ; and leave the 5th bit for the "ext sprite" loader. org $05D8FB AND.b #$1F ; Related for chocolate island? ; CODE_05DA8A: A7 CE LDA [$CE] ; CODE_05DA8C: 29 3F AND.B #$3F org $05DA8A JSL choco_island ; CODE_05DB5F: A7 CE LDA [$CE] ; CODE_05DB61: 29 7F AND.B #$7F org $05DB5F JSL choco_island ;This hijack is very bad, please use exfix_beb0.asm ;; CODE_00BEEC: C0 00 02 CPY.W #$0200 ;; CODE_00BEEF: B0 CA BCS ADDR_00BEBB ;; Is there any reason for restricting the Y position when generating ;; a specific tile? ; ;org $00BEEC ; BRA + ; NOP #3 ;+ ; $98 is actually Y pos. SMWDisC is wrong. ; CODE_00C1B1: A5 98 LDA RAM_BlockXLo ; CODE_00C1B3: 29 F0 01 AND.W #$01F0 ; Adjust to the maximum size. org $00C1B3 AND #$3FF0 ; CODE_00C079: A5 98 LDA RAM_BlockXLo ; CODE_00C07B: 29 F0 01 AND.W #$01F0 org $00C07B AND #$3FF0 ; CODE_00C0C6: A5 98 LDA RAM_BlockXLo ; CODE_00C0C8: 29 F0 01 AND.W #$01F0 org $00C0C8 AND #$3FF0 ; CODE_00C3D3: A5 98 LDA RAM_BlockXLo ; CODE_00C3D5: 29 F0 01 AND.W #$01F0 org $00C3D5 AND #$3FF0 ; CODE_058A16: B5 45 LDA $45,X ; CODE_058A18: 29 F0 01 AND.W #$01F0 ; VRAM upload stuff, probably not used anymore by smkdan's patch. org $058A18 AND #$3FF0 ; CODE_058AF2: B5 45 LDA $45,X ; CODE_058AF4: 29 F0 01 AND.W #$01F0 org $058AF4 AND #$3FF0 ; CODE_058BE4: B5 49 LDA $49,X ; CODE_058BE6: 29 F0 01 AND.W #$01F0 org $058BE6 AND #$3FF0 ; CODE_058CD7: B5 49 LDA $49,X ; CODE_058CD9: 29 F0 01 AND.W #$01F0 org $058CD9 AND #$3FF0 ; ******** Vitor: Remove hijacks, move to exsprites.asm ; ; CODE_01AC3E: B5 D8 LDA RAM_SpriteYLo,X ; \ ; ; CODE_01AC40: 18 CLC ; | ; ; CODE_01AC41: 69 50 ADC.B #$50 ; | if the sprite has gone off the bottom of the level... ; ; CODE_01AC43: BD D4 14 LDA.W RAM_SpriteYHi,X ; | (if adding 0x50 to the sprite y position would make the high byte >= 2) ; ; CODE_01AC46: 69 00 ADC.B #$00 ; | ; ; CODE_01AC48: C9 02 CMP.B #$02 ; | ; ; CODE_01AC4A: 10 34 BPL OffScrEraseSprite ; / ...erase the sprite ; ; That is how sprites currently check if one got off-screen. We will need to write ; ; to we use the custom level size. To discretely don't have to manipulate the sprite tables, ; ; we will put a XBA after the first load and hijack the ADC/CMP. ; org $01AC40 ; NOP #2 ; Reduce the risk of eventual hijack conflict over the LDA opcode. ; XBA ; org $01AC46 ; JML sub_off_screen_hack_1 ; ; CODE_02D038: B5 D8 LDA RAM_SpriteYLo,X ; \ ; ; CODE_02D03A: 18 CLC ; | ; ; CODE_02D03B: 69 50 ADC.B #$50 ; | if the sprite has gone off the bottom of the level... ; ; CODE_02D03D: BD D4 14 LDA.W RAM_SpriteYHi,X ; | (if adding 0x50 to the sprite y position would make the high byte >= 2) ; ; CODE_02D040: 69 00 ADC.B #$00 ; | ; ; CODE_02D042: C9 02 CMP.B #$02 ; | ; ; CODE_02D044: 10 34 BPL OffScrEraseSprBnk2 ; / ...erase the sprite ; org $02D03A ; NOP #2 ; XBA ; org $02D040 ; JML sub_off_screen_hack_2 ; ; This is for cluster sprites. ; ; ADDR_02FED3: BD 02 1E LDA.W $1E02,X ; ; ADDR_02FED6: 18 CLC ; ; ADDR_02FED7: 69 50 ADC.B #$50 ; ; ADDR_02FED9: BD 2A 1E LDA.W $1E2A,X ; ; ADDR_02FEDC: 69 00 ADC.B #$00 ; ; ADDR_02FEDE: C9 02 CMP.B #$02 ; ; ADDR_02FEE0: 10 2C BPL ADDR_02FF0E ; org $02FED6 ; NOP #2 ; XBA ; org $02FEDC ; JML sub_off_screen_hack_3 ; ; CODE_03B86A: B5 D8 LDA RAM_SpriteYLo,X ; \ ; ; CODE_03B86C: 18 CLC ; | ; ; CODE_03B86D: 69 50 ADC.B #$50 ; | if the sprite has gone off the bottom of the level... ; ; CODE_03B86F: BD D4 14 LDA.W RAM_SpriteYHi,X ; | (if adding 0x50 to the sprite y position would make the high byte >= 2) ; ; CODE_03B872: 69 00 ADC.B #$00 ; | ; ; CODE_03B874: C9 02 CMP.B #$02 ; | ; ; CODE_03B876: 10 34 BPL OffScrEraseSprBnk3 ; / ...erase the sprite ; org $03B86C ; NOP #2 ; XBA ; org $03B872 ; JML sub_off_screen_hack_4 ; $09 - is vertical level flag for current layer. ; LDA $09 ;$00BF31 | ; AND.b #$01 ;$00BF33 | ; BEQ CODE_00BF41 ;$00BF35 | ; LDA $99 ;$00BF37 | ; LSR ;$00BF39 | ; LDA $9B ;$00BF3A | ; AND.b #$01 ;$00BF3C | ; JMP CODE_00BF46 ;$00BF3E | ;CODE_00BF41: ; LDA $9B ; LSR ;$00BF43 | ; LDA $99 ;$00BF44 | ;CODE_00BF46: ; ROL ; ASL ;$00BF47 | ; ASL ;$00BF48 | ; ORA.b #$20 ;$00BF49 | ; STA $04 ;$00BF4B | ;********* end ; The above piece of code is responsible for calculating the VRAM offset for ; stripe image upload of layer 1/2 tiles. However, while for vertical levels ; the Y position is properly masked, the same does not happen with horizontal ; levels, which depending of the Y position the coin or whatever collectable is ; collected the routine will wrongly calculate it as layer 2 instead of layer 1. ; This moves the AND to the end of the routine to affect both codes, at cost of ; some code shifting. org $00BF35 BEQ stripe_horz org $00BF3C ; JMP stripe_horz_skip BRA stripe_horz_skip ;FuSoYa: This is easier NOP ;(and this) stripe_horz: LDA $9B LSR LDA $99 .skip ROL ASL ASL AND #$0C ; ORA #$20 ;FuSoYa: This is already here ; STA $04 ;warnpc $00BF4D warnpc $00BF49 ;FuSoYa: Now goes to here ; CODE_0295BB: AD EC 13 LDA.W $13EC ; CODE_0295BE: 69 00 ADC.B #$00 ; CODE_0295C0: C9 02 CMP.B #$02 ; CODE_0295C2: B0 E9 BCS Return0295AD ; Cape interation on horizontal levels goes until Y <= #$01FF. ; This hack makes it check against !Screen_Size instead. org $0295BE JML cape_interact ;org $0C93B5 ; JSL load_table_credits ; NOP ; LDA #$23 ; STA $0100 ; CODE_0C9436: 85 65 STA $65 ; CODE_0C9438: E2 30 SEP #$30 ; Index (8 bit) Accum (8 bit) org $0C9436 JSL load_table_credits ;LoadSpriteLoopStrt: B7 CE LDA [$CE],Y ; Byte format: YYYYEEsy ;CODE_02A830: C9 FF CMP.B #$FF ; \ Return when we encounter $FF, as it signals the end ;CODE_02A832: F0 17 BEQ Return02A84B ; / ;TO DO: hack 02A830 or 02A84B to add the extra byte for #$FF. freecode ; Format: %toPsssss ; sssss => screen size mode (00 - 1F) ; o => overscan level mode. If set, the camera scrolls slight more. ; t => two layer flag. If set, it means the level has two interactive layers. ; P => ext sprite spawn flag. It's set by the sprite header so it shouldn't be used here. level_settings: db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 000-00F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 010-01F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 020-02F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 030-03F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 040-04F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 050-05F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 060-06F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 070-07F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 080-08F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 090-09F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 0A0-0AF. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 0B0-0BF. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 0C0-0CF. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 0D0-0DF. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 0E0-0EF. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 0F0-0FF. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 100-10F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 110-11F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 120-12F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 130-13F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 140-14F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 150-15F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 160-16F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 170-17F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 180-18F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 190-19F. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 1A0-1AF. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 1B0-1BF. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 1C0-1CF. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 1D0-1DF. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 1E0-1EF. db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Levels 1F0-1FF. ; These are the screen vertical sizes. I picked the ones that are the most useful ; and does the best space usage to fit a certain number of screens. ; Optimal are sizes that uses 100% of the map16 table without leftovers. You probably ; are aware that the original horizontal level layout didn't even use all of the map16 ; available space... ; The last three ones are not intended to be used but are still available for who want ; to use part of the unused data as free RAM.... but it may glitch of course because ; the patch is not designed for allocating more than 0x20 screens. screen_sizes: dw $01B0 ; #$00 - max 0x20 screens [$200 x $01B = $3600 = original] dw $01C0 ; #$01 - max 0x20 screens [$200 x $01C = $3800 = optimal] dw $01D0 ; #$02 - max 0x1E screens [$1E0 x $01D = $3660] dw $0200 ; #$03 - max 0x1C screens [$1C0 x $020 = $3800 = optimal] dw $0220 ; #$04 - max 0x1A screens [$1A0 x $022 = $3740] dw $0250 ; #$05 - max 0x18 screens [$180 x $025 = $3780] dw $0260 ; #$06 - max 0x17 screens [$170 x $026 = $36A0] dw $0280 ; #$07 - max 0x16 screens [$160 x $028 = $3700] dw $02A0 ; #$08 - max 0x15 screens [$150 x $02A = $3720] dw $02C0 ; #$09 - max 0x14 screens [$140 x $02C = $3700] dw $02F0 ; #$0A - max 0x13 screens [$130 x $02F = $37D0] dw $0310 ; #$0B - max 0x12 screens [$120 x $031 = $3720] dw $0340 ; #$0C - max 0x11 screens [$110 x $034 = $3740] dw $0380 ; #$0D - max 0x10 screens [$100 x $038 = $3800 = optimal] dw $03B0 ; #$0E - max 0x0F screens [$0F0 x $03B = $3750] dw $0400 ; #$0F - max 0x0E screens [$0E0 x $040 = $3800 = optimal] dw $0440 ; #$10 - max 0x0D screens [$0D0 x $044 = $3740] dw $04A0 ; #$11 - max 0x0C screens [$0C0 x $04A = $3780] dw $0510 ; #$12 - max 0x0B screens [$0B0 x $051 = $37B0] dw $0590 ; #$13 - max 0x0A screens [$0A0 x $059 = $37A0] dw $0630 ; #$14 - max 0x09 screens [$090 x $063 = $37B0] dw $0700 ; #$15 - max 0x08 screens [$080 x $070 = $3800 = optimal] dw $0800 ; #$16 - max 0x07 screens [$070 x $080 = $3800 = optimal] dw $0950 ; #$17 - max 0x06 screens [$060 x $095 = $37E0] dw $0B30 ; #$18 - max 0x05 screens [$050 x $0B3 = $37F0] dw $0E00 ; #$19 - max 0x04 screens [$040 x $0E0 = $3800 = optimal] dw $12A0 ; #$1A - max 0x03 screens [$030 x $12A = $37E0] dw $1C00 ; #$1B - max 0x02 screens [$020 x $1C0 = $3800 = optimal] [same as vertical] dw $3800 ; #$1C - max 0x01 screen [$010 x $380 = $3800 = optimal] [max vert] dw $0100 ; #$1D - max 0x38 screens [$380 x $010 = $3800 = optimal] [may glitch] dw $00F0 ; #$1E - max 0x3B screens [$3B0 x $00F = $3750] [may glitch] dw $00E0 ; #$1F - max 0x40 screens [$400 x $00E = $3800 = optimal] [max horz] [may glitch] choco_island: STZ $1D ; \ COMBO FIX: Reset upper bytes of the scroll values STZ $21 ; / ; "The latter is to fix a glitch in the ; original game for BGs in No Yoshi entrances to vertical levels, as ; Nintendo didn't set the upper byte like they should have. The former ; on the other hand is to fix the same thing for FGs with levels that ; use the new FG/BG init system, as I'm turning off the hack Nintendo ; used to fix broken FG init positions in vertical levels. I could put ; these in a separate new hijack, but we might as well bundle it with ; the one you've already got there." STZ.w !ExLevel_Flags ; Reset flags. PHX REP #$30 ; 16-bit A. LDA #$01B0 ; \ Set screen size to #$01B0. STA.w !Screen_Size ; | SEC ; | SBC #$0010 ; | STA.w !Screen_Size_L ; / LDA #$C800 ; \ Build table. This makes sure LDX #$0000 ; | special levels such as No Yoshi, JSR build_table ; / Choco Island levels, etc., are properly built. LDA #$0000 ; \ Make sure upper A is zero. SEP #$30 ; / 8-bit. PLX LDA [$CE] ; \ Restore code and return. AND #$1F ; | but AND by #$1F instead. RTL ; / load_table_credits: STA $65 ; Restore code . . . LDA #$01B0 ; \ Set screen size to #$01B0. STA.w !Screen_Size ; | SEC ; | SBC #$0010 ; | STA.w !Screen_Size_L ; / LDA #$C800 ; \ Build table. This makes sure LDX #$0000 ; | special levels such as No Yoshi, JSR build_table ; / Choco Island levels, etc., are properly built. SEP #$30 ; 8-bit (restore code as well). STZ.w !ExLevel_Flags ; Reset flags. RTL ; Vitor: edited and moved to exsprites.asm ; sub_off_screen_hack_1: ; XBA ; REP #$20 ; CMP.w !Screen_Size ; SEP #$20 ; JML $01AC4A ; sub_off_screen_hack_2: ; XBA ; REP #$20 ; CMP.w !Screen_Size ; SEP #$20 ; JML $02D044 ; sub_off_screen_hack_3: ; XBA ; REP #$20 ; CMP.w !Screen_Size ; SEP #$20 ; JML $02FEE0 ; sub_off_screen_hack_4: ; XBA ; REP #$20 ; CMP.w !Screen_Size ; SEP #$20 ; JML $03B876 iggy_larry: LDA $0D9B ; \ If we're on Iggy or Larry CMP #$80 ; | boss battle, do not run BEQ .return ; / cape <-> object interaction STZ $0F ; \ Otherwise, run the routine PEA.w $9510-1 ; | normally. JML $029540 ; / .return JML $02953B ; Jump to the routine RTS. cape_interact: ADC #$00 CMP.w !Screen_Size+1 BNE .done XBA LDA $00 CMP.w !Screen_Size+0 XBA .done JML $0295C2 max_scroll: ; carry is not set. LDA.w !ExLevel_Flags AND #$0040 ; \ Test for over screen and adjust A. BEQ + ; | LDA #$000F ; / + ADC.w !Screen_Size ; \ Get the screen size and subtract by #$00F0 SBC #$00EF ; / or by #$00E1 if over screen flag is set. PEA.w $F713-1 ; \ Return and JSR to $00F7F4. JML $00F7F4 ; / screen_load_hack: LDA [$CE] ; \ Load sprite header AND #$20 ; | and set the ext level flags, STA.w !ExLevel_Flags ; / if needed. LDA $5B ; \ If the level is not vertical, AND #$01 ; | branch to custom code. BEQ .not_vertical ; / REP #$20 LDA #$01B0 STA.w !Screen_Size SEC SBC #$0010 STA.w !Screen_Size_L PHX LDA #$C800 ; \ Build table, to make sure there is no (even more) LDX #$0000 ; | undefined behavior if a certain code accesses JSR build_table ; / vertical level table as horizontal... PLX LDA #$0100 ; \ COMBO FIX: make vertical levels STA.w !Screen_Size ; / horizontal boundary works as intended. LDA #$0000 SEP #$20 LDA #$01 ; \ Branch and return. RTL ; / .not_vertical TYX ; X now has level number. ; Vitor: load these additional bits ; for exsprites.asm LDA.l $05DE00,x ; -----UVV AND #$07 ; 00000UVV BIT #$04 ; \ BEQ + ; | U00000VV EOR #$84 ; / + STA.w !ExSprite_Flags ; Store to ExSprite flags... LDA.l level_settings,x ; \ Load ex-level flags and TSB.w !ExLevel_Flags ; / store to the settings. REP #$20 AND #$001F ASL TAX LDA.l screen_sizes,x STA.w !Screen_Size SEC SBC #$0010 STA.w !Screen_Size_L LDA #$C800 LDX #$0000 JSR build_table SEP #$20 ; Check if this is a layer 2 level. BIT.w !ExLevel_Flags BPL .no_layer2 ; Not required because the lookup does not need adjust. CPX #$0060 BCS .no_layer2 ; If it is a layer 2 level, correct the table ; by building the other screens to the correct place. REP #$20 TXA BIT #$0001 BEQ + SEC SBC #$0003 + LSR TAX LDA.w !L1_Screen_Lo,x LDX #$0030 JSR build_table SEP #$20 .no_layer2 PHY ; Split the 24-bit lookup table into the 8-bit ones that are used ; mostly for interaction between the sprites and the player with the ; ground. LDX #$005D LDY #$001F - LDA.w !L1_Screen_Lo+0,x STA.w !L1_Lookup_Lo,y LDA.w !L1_Screen_Lo+1,x STA.w !L1_Lookup_Hi,y DEX #3 DEY BPL - PLY ; Finish the 24-bit lookup table by inserting the bank table on them. LDA #$7E LDX #$005F - STA !L1_Screen_Lo,x DEX #3 BPL - LDA #$7F LDX #$005F - STA !L1_Screen_Hi,x DEX #3 BPL - ; The branch is skipped because we are on a horizontal level. ; See the bit CMP for context. LDA #$00 ; \ Make sure high byte is zero... XBA ; / LDA #$00 ; \ Return and skip next branch. RTL ; / ; Build Map16 screen lookup table base. build_table: CLC .fill_ram_loop STA.w !L1_Screen_Lo,x STA.w !L1_Screen_Hi,x ADC.w !Screen_Size INX #3 BCS .overflow ; CMP #$FF00 ;SMAS+W, cannot go over this ; BCS .overflow ;BGE CPX #$0060 BCC .fill_ram_loop .overflow RTS ; Vitor: Comment string for knowing if the patch is installed and which "generation" is ; (in case we ever consider the irregular levels idea eventually). 12 bytes should be ok. db "EXLEVEL-GEN1" db " " db "LM" db $00,$01 ;LM integrated patch version 1.00 (LM version 2.60+) org $00BDA8 ; These pointers will remap the Map16 per screen table to the RAM, which is the main ; key behind the patch: we can dynamically modify the table, keeping compatibility with ; pretty much all ASM hacks and original game map16 modifications. Ptrs00BDA8: dw !L1_Screen_Lo dw !L1_Screen_Lo dw !L1_Screen_Lo dw $0000 dw $0000 dw $0000 dw $0000 dw $BBEC dw $BBEC dw $0000 dw $BBEC dw $0000 dw !L1_Screen_Lo dw $BBEC dw !L1_Screen_Lo dw !L1_Screen_Lo dw $0000 dw !L1_Screen_Lo dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw !L1_Screen_Lo dw !L1_Screen_Lo Ptrs00BDE8: dw !L2_Screen_Lo dw !L2_Screen_Lo dw !L2_Screen_Lo dw $0000 dw $0000 dw $0000 dw $0000 dw $BC16 dw $BC16 dw $0000 dw $BC16 dw $0000 dw !L2_Screen_Lo dw $BC16 dw !L2_Screen_Lo dw !L2_Screen_Lo dw $0000 dw !L2_Screen_Lo dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw !L2_Screen_Lo dw !L2_Screen_Lo Ptrs00BE28: dw !L1_Screen_Hi dw !L1_Screen_Hi dw !L1_Screen_Hi dw $0000 dw $0000 dw $0000 dw $0000 dw $BD54 dw $BD54 dw $0000 dw $BD54 dw $0000 dw !L1_Screen_Hi dw $BD54 dw !L1_Screen_Hi dw !L1_Screen_Hi dw $0000 dw !L1_Screen_Hi dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw !L1_Screen_Hi dw !L1_Screen_Hi Ptrs00BE68: dw !L2_Screen_Hi dw !L2_Screen_Hi dw !L2_Screen_Hi dw $0000 dw $0000 dw $0000 dw $0000 dw $BD7E dw $BD7E dw $0000 dw $BD7E dw $0000 dw !L2_Screen_Hi dw $BD7E dw !L2_Screen_Hi dw !L2_Screen_Hi dw $0000 dw !L2_Screen_Hi dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw $0000 dw !L2_Screen_Hi dw !L2_Screen_Hi