Language…
8 users online: 1392year, Alice3173, DasFueller, drkrdnk, Firstnamebutt, GRIMMKIN, Mario's GameBase,  NinCollin - Guests: 152 - Bots: 367
Users: 64,795 (2,381 active)
Latest user: mathew

Advanced Documentation and ASM Code Library

Yeah, as you start adding more and more "sections" of scanlines, it becomes more and more time consuming for the SNES to process. Adding to $01B0 is only required since a horizontal level is $01B0 pixels tall.
Is there a more easier way to know what certain values to raise or lower to avoid? It gets confusing, as I started on to put $0030 and just guess
For stimulating more posts here, I decided to post some codes that I have.
Note that they're supposed to work only with Asar and all of them should be compatible with SA-1 unless stated.

But seriously, this thread can be extremely useful for all SMW ASM coders. I'm tired of searching for code from SMWDisC and from other sprites and blocks D:

1. "long" version of branches. Useful if you don't want to make your code ugly if some branch get out of range.

Code
macro jeq(branch)
	bne ?skip
	jmp <branch>
?skip:
endmacro

macro jne(branch)
	beq ?skip
	jmp <branch>
?skip:
endmacro

macro jcc(branch)
	bcs ?skip
	jmp <branch>
?skip:
endmacro

macro jcs(branch)
	bcc ?skip
	jmp <branch>
?skip:
endmacro

macro jpl(branch)
	bmi ?skip
	jmp <branch>
?skip:
endmacro

macro jmi(branch)
	bpl ?skip
	jmp <branch>
?skip:
endmacro

macro jvc(branch)
	bvs ?skip
	jmp <branch>
?skip:
endmacro

macro jvs(branch)
	bvc ?skip
	jmp <branch>
?skip:
endmacro


2. Toggle/XOR carry flag:

Code
macro xoc()
	bcc ?set
	clc
	bra ?done
?set:	sec
?done:
endmacro


3. Store/Add/Subtract/Compare 32-bit numbers. (Thanks Alcaro for some help). Assuming that you're using 16-bit A.

Code
macro cmp32(a,b)
	lda <a>+2
	cmp <b>+2
	bne ?l
	lda <a>
	cmp <b>
?l:
endmacro

macro add32(a,b)
	lda <a>
	clc
	adc <b>
	sta <a>
	lda <a>+2
	adc <b>+2
	sta <a>+2
endmacro

macro sub32(a,b)
	lda <a>
	sec
	sbc <b>
	sta <a>
	lda <a>+2
	sbc <b>+2
	sta <a>+2
endmacro

macro set32(a,b)
	lda <b>
	sta <a>
	lda <b>+2
	sta <a>+2
endmacro


4. Play victory theme. Call %victory() to make Mario walk. Call %victory() AND %victory2() to make Mario don't walk. Note that the code assumes that you're using AddmusicK.

Code
macro victory()
	lda #$ff
	sta $1493
	sta $0dda
	lda #$03
	sta $1dfb
endmacro

macro victory2()
	dec $13c6
endmacro


SA-1:

Code
macro victory()
	lda #$ff
	sta $7493
	sta $6dda
	lda #$03
	sta $7dfb
endmacro

macro victory2()
	dec $73c6
endmacro


5. Easy "execute" pointer. I got tired of boring table setup and since executeptr is rather slow, I created a macro to make them easier and neater to read:

Code
macro exec(diff,jump)
	asl
	tax
	pea.w <jump>-1
	jmp (?label-<diff>,x)
?label:
endmacro


diff is how many bytes to the jump shift from the table. It's supposed to be a negative table. Useful if A doesn't goes from 0. For example, if A always starts from 1, use -2.

jump is the label/address to return after jumping.

Of course the label jumped should return with RTS.

Example:

Code
	lda $3523
	%exec(2,back)
	dw backup
	dw upload
	dw gfx1
	dw gfx2
back:	;*other code*;


6. (SA-1 only) Invoke SA-1 macro:

Code
macro invoke_sa1(label)
	LDA.b #<label>
	STA $3180
	LDA.b #<label>>>8
	STA $3181
	LDA.b #<label>>>16
	STA $3182
	JSR $1E80
endmacro


Pretty self-explanatory. Example:

Code
level105:
	%invoke_sa1(.sa1_code)
	RTS

.sa1_code:
	PHB
	PHK
	PLB
	;*code*;
	PLB
	RTL


7. Status Bar RAM address to VRAM address. Useful if you want to convert a status bar address ($0EF9) to VRAM direct, for editing the properties (palette,priority,x/y flip) of status bar on NMI for example.

Code
macro statusbar_vram(addr)
	if <addr> < $0F15
		LDA.w #$5042+<addr>-$0EF9
	else
		LDA.w #$5063+<addr>-$0F15
	endif
endmacro


8. Find OAM slot. Useful for uberASM codes. Should work in both level and overworld. Note that it may have weird effects if you don't use (and enable) NMSTL:

Code
FindOAM:
	LDY #$FC
-	LDA $02FD,y
	CMP #$F0
	BNE +
	CPY #$3C
	BEQ +
	DEY
	DEY
	DEY
	DEY
	BRA -
+	RTS


SA-1:
Code
FindOAM:
	LDY #$FC
-	LDA $62FD,y
	CMP #$F0
	BNE +
	CPY #$3C
	BEQ +
	DEY
	DEY
	DEY
	DEY
	BRA -
+	RTS


That's all.
Oh man. Seeing these macros makes me want to do asm again.
I own a community of TF2 servers!

ASMT - A new revolutionary ASM system, aka 65c816 ASseMbly Thing
SMWCP - SMW Central Presents a Product- tion long name

frog

http://esolangs.org/wiki/MarioLANG
Yeah. You know what would be cool? Some kinda of macro library for SMW that can be downloaded and gets updated regularly. And then maybe some kind of IDE for asar that can read this library and lets you find functions easily. Kinda like like the standard library of C++ or like .NET Framework in C#.
Feel free to visit my website/blog - it's updated rarely, but it looks pretty cool!


Originally posted by RPG Hacker
Yeah. You know what would be cool? Some kinda of macro library for SMW that can be downloaded and gets updated regularly. And then maybe some kind of IDE for asar that can read this library and lets you find functions easily. Kinda like like the standard library of C++ or like .NET Framework in C#.


Technically, it's pretty easy to do and anyone can do it, as asar has an "include" directive (and "includefrom", if I'm not mistaken). So, we just have to arrange the macros in a file and done. Of course, asar could be changed a little bit so the macro library can be used by any code. Kinda similar to io library in x86 assembly.





Dream team (feed them, please):






Yeah, but it doesn't have a good search function. And first we need such a huge library, anyways (preferably with a huge, detailed documentaiton).
Feel free to visit my website/blog - it's updated rarely, but it looks pretty cool!
This is a behavior algorithm I created. It was inspired by something HuFlungDu mentioned about his SMWCP2 bosses, but it wasn't based off any of his code (which I never saw anyway). Basically, this chooses an attack or other behavior from a pool of possible ones, and the longer the sprite goes without doing a particular one, the more likely it is that that one will come up next.

Code
ChooseBehavior:
	PHX
	LDA !T
	INC
	STA $00
	LDA !R2
	DEC
	JSR RangedRandomRt
	STA $01
	LDX #$01
	LDY #$00
.Loop
	LDA $01
	CMP $00
	BCC .Break
	LDA $00
	SEC
	ADC !T,x
	STA $00
	BCS .UseLast
	INX
	INY
	CPY !R1
	BCC .Loop
.UseLast
	LDY !R1
	DEY
.Break
	LDA #$FF
	STA !T,y
	STY $02
	LDX #$00
.Loop2
	INC !T,x
	INX
	CPX !R1
	BCC .Loop2
	LDX #$00
	TXA
.Loop3
	SEC
	ADC !T,x
	BCS .SetMax
	STA !R2
	INX
	CPX !R1
	BCC .Loop3
	BRA .NotMax
.SetMax
	LDA #$FF
	STA !R2
.NotMax
	PLX
	LDA $02
	RTS

!R1 and !R2 should be single-byte free RAM addresses, and !T should be a table in free RAM that is as long as the total number of different behaviors you want to select from. For instance, if you have 6 possible behaviors, this should be 6 bytes. I used part of the RAM at $1BA3 when I was testing it, since as far as I know, that table does nothing outside of Mode 7 levels and should be usable as temporary free RAM. Also, each byte of !T should be initialized to 00, and both !R1 and !R2 should be initialized to the total behavior count (so if you have 6 different ones, put #$06 here). I haven't used this in any real sprites yet, but I ran it through a test sprite, and it seemed to work fine. If anyone has any suggestions for improving it, though, feel free to mention them. (Also, I do have a version with comments, but I left them out of it here because they were misaligned.)

----------------

I'm working on a hack! Check it out here. Progress: 64/95 levels.
Code
;Determine which side of the block mario is touching 2, by GreenHammerBro.

;Unlike the previous version, this one uses $7E:009A (player X position touching
;the block), rather than using $7E:0077 (player blocked status, when touching
;solid objects). Making it compatable with horizontally moving layer 2 blocks
;or making it behave $25.

MarioSide:
	REP #$20		;>begin 16-bit mode
	LDA $9A			;\the block position
	AND #$FFF0		;/
	CMP $94			;\if block is right of mairo (if mario is hitting the
	SEP #$20		;|left side), then branch to left side.
	BPL left_side		;/(end 16-bit mode)

;right_side:
	;[right side code here]
	RTL
left_side:
	;[left side code here]
	RTL


Code
;Trigger if moving against the side of the block, by GreenHammerBro.

;Not to be confused with triggering if touching the side of the block,
;like the muncher or spikes from smw, this code will only run if mario
;is forced into or moving towards the block by either X speed or if
;mario's X position has been incremented/decremented towards (by
;"wind" or "conveyor belt") the side. If mario isn't moving against
;the side, it will do nothing, reguardless if touching the side or
;not.


MarioSide:

	REP #$20		;\If mario's block hitbox is not at least a
	LDA $9A			;|pixel overlapping the block's hitbox,
	AND #$FFF0		;|then return.
	SEC			;|
	SBC #$000E		;|
	CMP $94			;|
	BPL Return		;|
	CLC			;|
	ADC #$001A		;|> this means #$0C if starting at #$00
	CMP $94			;|
	BMI Return		;|
	SEP #$20		;/

	;[Insert your code here]
Return:
	SEP #$20
	RTL


Code
;Fixed multiplication routine (the original did do X*(Y+1) rather than X*Y).
;EDIT: found and fix a glitch where if $02 is #$00, it multiplies $00 by #$01
;instead. So make sure you check if $02 is #$00/#$0000 before executing this
;routine.
;input:
;$00 (8/16-bit) = 1st multiplicand (assumes this is 1 if 0).
;$02 and $04 (8/16-bit) = 2nd multiplicand

;Output:
;A = product

ExampleInput:
	REP #$20
	LDA XXXX        ;\1st multiplicand
	STA $00         ;/
	LDA YYYY        ;\2nd multiplicand
	STA $02         ;|
	STA $04         ;/>Prevent 1 extra addition
	BEQ .Zero       ;>If 2nd multiplicand = 0, make result 0
	JSR Multiply    ;>Execute multiplication
	BRA .SkipZero   ;>And don't replace result
.Zero
	LDA #$0000      ;>Load as zero result.
.SkipZero
	;[some codes here]	;>Use "A" register for product
	RTS/RTL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Multiply:				;\multiplication subroutine
	STA $04				;|
.Loop	LSR $02				;|
	BEQ .end			;|
	BCC .Next			;|
	CLC				;|
	ADC $00				;|
.Next	ASL $00				;|
	BRA .Loop			;|
.end					;|
	CLC				;|
	ADC $00				;|
	SEC				;|\remove 1-extra multiplication.
	SBC $04				;//
	RTS


EDIT: superseded by this (both are unsigned):
Code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 16bit * 16bit Multiplication
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Argusment
; $00-$01 : Multiplicand
; $02-$03 : Multiplier
; Return values
; $04-$07 : Product
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

MathMul16_16:	REP #$20
		LDY $00
		STY $4202
		LDY $02
		STY $4203
		STZ $06
		LDY $03
		LDA $4216
		STY $4203
		STA $04
		LDA $05
		REP #$11
		ADC $4216
		LDY $01
		STY $4202
		SEP #$10
		CLC
		LDY $03
		ADC $4216
		STY $4203
		STA $05
		LDA $06
		CLC
		ADC $4216
		STA $06
		SEP #$20
		RTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 16bit * 16bit Multiplication SA-1 version
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Argusment
; $00-$01 : Multiplicand
; $02-$03 : Multiplier
; Return values
; $04-$07 : Product
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

MathMul16_16:	STZ $2250
		REP #$20
		LDA $00
		STA $2251
		ASL A
		LDA $02
		STA $2253
		BCS +
		LDA.w #$0000
+		BIT $02
		BPL +
		CLC
		ADC $00
+		CLC
		ADC $2308
		STA $06
		LDA $2306
		STA $04
		SEP #$20
		RTS


Edit: fix the "Trigger if moving against the side of the block" glitch where if placed on the leftmost edge of level.
Give thanks to RPG hacker for working on Asar.


Here's some little code I developed: Division routine.

Code

;
; Use:
; REP #$20
; LDA.w #$aaaa
; STA $00
; LDA.w #$bbbb
; STA $02
; PHB
; PHK
; PLB
; JSR Divide
; PLB
;
; At any moment, you'll want a SEP #$20
;
; $04 : Quotient (16-bit)
; $06 : Remainder (16-bit)
; Y   : Error Flag (divided by zero)

Divide:
	LDY #$00
	STZ $04
	STZ $06
	LDA $02
	BNE .Continue
	INY
	RTS
.Continue
	LDX #$0F
.Loop				; \
	CPX #$00		; | For i=15...0
	BMI .EndLoop		; /

	LDA $06			; \
	ASL A			; | R <<=1
	STA $06			; /

	LDA $00
	JSR NewGetBit
	ORA $06
	STA $06

	LDA $06
	CMP $02	
	BCC .Next
	
	LDA $06
	SEC
	SBC $02
	STA $06

	;LDA.w #$0001
	JSR NewMultiplyBit
	ORA $04
	STA $04


.Next
	DEX
	JMP .Loop
.EndLoop
	LDY.b #$00
	RTS

NewGetBit:
	PHX
	PHY
	LDY.b #$00
	PHA
	TXA 
	ASL
	AND.w #$00FF
	TAX
	PLA
	AND.w Look_Table,x
	BEQ +
	INY
+
	TYA
	PLY
	PLX
	RTS

NewMultiplyBit:
	PHX
	TXA
	ASL
	AND.w #$00FF
	TAX
	LDA Look_Table,x
	PLX
	RTS

Look_Table:
	dw $0001,$0002,$0004,$0008,$0010,$0020,$0040,$0080
	dw $0100,$0200,$0400,$0800,$1000,$2000,$4000,$8000






Dream team (feed them, please):






Sprite slot debugger.

Shows the slots that are occupied and unoccupied on HUD, useful to tell of all the slots are filled.

please uploate it here
Edit: actually it starts at different slots depending on the sprite memory.
Give thanks to RPG hacker for working on Asar.
So, where do I begin...
This is more or less useless, or at least not as usefull as it could be by making it display less information on purpose.

First and formost, any person who'd want to debug their sprite slots would probably just use the RAM display of snes9x debugger. It's a lot easier than going about inserting code.

As for the code itself.
Why do you STZ all at the beginning? And why do you only store 1 if the slot is occupied?
There are only 00-0C different values for $14C8, that is if nothing stores garbage there.
So instead of doing all that you could just loop over the table and store it to the status bar:

Code
	LDX #!sa1_slots-1	; init X
-
	LDA $14C8,x		;\ get sprite state
	STA $0EF9,x		;/ to status bar
	DEX			
	BPL -			; x still positive? -> loop

That way you can not only tell if the slot is occupied but alos what state that sprite is in. But as I said, Anybody who'd want that information would just go use the real debugger.
And seeing as that itself is more or less just a simple loop, there is no real need to put it in the library... unless Alcaro decides otherwise.
Anime statistic on MyAnimeList:
400 animes completed ✓
6000 episodes completed ✓
100 Days completed ✓
... what even am I doing with my life?
This is a routine I created for simulating spritesets with more but smaller GFX files, similar to what Yoshi's Island does. The advantage it has over SMW's existing spriteset system is that it is much easier to mix spritesets, because there are 8 GFX files of size 1 KiB instead of 2 GFX files of size 4 KiB. (It only handles the second GFX page, which would normally be SP3 and SP4.) JSR to this in uberASM's game mode 13 init. The GFX file number list is optional; it is not necessary for the routine to function, but it can be used in other things, such as the custom sprites themselves, to do as YI does and change the tilemap depending on which GFX files are loaded where. (Set the define to whatever free RAM address you want as long as it is at least 16 bytes long; I used $7FA300 in my test ROM.) Also, here is a patch that will remap all sprite tiles (or at least most of them? I might have missed a few...) to match the new tilemaps.

(Edit: Updated post with note about the other decompression routine and added a link to the sprite remap hex edit patch.)

Code
!RAM_SpriteGFXList = $7FA300

SpriteSetHandler:
	PHP
	REP #$30
	LDY $010B
	LDA LevelSpriteSet,y
	AND #$00FF
	ASL : ASL : ASL : ASL
	TAY
	LDX #$0000
.Loop
	LDA SpriteSetGFXList,y
	STA !RAM_SpriteGFXList,x
	JSR DecompressGFXFile
	TXA
	XBA
	EOR #$7E00
	JSR UploadVRAMData
	INY : INY
	INX : INX
	CPX #$000E
	BCC .Loop
	PLP
	RTS

LevelSpriteSet:
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00	; levels 0-F
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00	; levels 10-1F
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00	; levels 20-2F
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00	; levels 30-3F
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00	; levels 40-4F
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00	; levels 50-5F
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00	; levels 60-6F
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00	; levels 70-7F
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00	; levels 80-8F
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00	; levels 90-9F
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00	; levels A0-AF
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00	; levels B0-BF
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00	; levels C0-CF
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00	; levels D0-DF
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00	; levels E0-EF
	db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00	; levels F0-FF
	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

SpriteSetGFXList:
	dw $0080,$0081,$0082,$0083,$0084,$0085,$0086,$0087		; spriteset 00
	dw $0080,$0081,$0082,$0083,$0088,$0089,$008A,$0087		; spriteset 01
	dw $0080,$0081,$0082,$008B,$008C,$008D,$008E,$008F		; spriteset 02
	dw $0080,$0090,$0091,$0092,$0093,$0094,$0095,$0096		; spriteset 03
	dw $0080,$0081,$0082,$0083,$0099,$009A,$009B,$00A5		; spriteset 04
	dw $0080,$0081,$0082,$0083,$009C,$009D,$009E,$009F		; spriteset 05
	dw $0080,$0081,$0082,$0092,$00A0,$00A1,$00A2,$00A3		; spriteset 06
	dw $0080,$0081,$0082,$0083,$0099,$00A7,$00A4,$00A5		; spriteset 07	
;	dw $007F,$007F,$007F,$007F,$007F,$007F,$007F,$007F		; spriteset 08
;...
;	dw $007F,$007F,$007F,$007F,$007F,$007F,$007F,$007F		; spriteset FF


You will also need these supplementary routines for decompressing and uploading the GFX files, if you aren't using them already. In fact, now that I think about it, why aren't these in the library already? They have already proven useful for quite a number of things. The define here is not optional this time, but again, set it to anything big enough. I used $7F1500 in my test ROM, since $7F1500-$7F3FFF would be the same size as SMW's normal decompression buffer at $7EAD00 when combined with Lunar Magic's ASM hacks. (And if you're wondering why I didn't just use $7EAD00 then, it's because Lunar Magic—rather foolishly—also uses that space for ExAnimation data, so using it as a decompression buffer after that gets loaded will cause any animation in that level that uses the extra animated tile space to glitch up.)

Alternatively, you can just use $0FF900, as Vitor Vilela pointed out in the following post.

Code
!DecompBuffer = $7F1500

DecompressGFXFile:
	PHY
	PHX
	PHP
	REP #$30
	CMP #$0032
	BCC .GFX00to31
	CMP #$0080
	BCC .UploadReturn
	CMP #$0100
	BCS .ExGFX100toFFF
.GFX80toFF
	AND #$007F
	STA $8A
	ASL
	CLC
	ADC $8A
	TAY
	LDA $0FF94F
	STA $06
	LDA $0FF950
	STA $07
	BRA .FinishDecomp
.UploadReturn
	PLP
	PLX
	PLY
	RTS
.GFX00to31
	TAX
	LDA $00B992,x
	STA $8A
	LDA $00B9C4,x
	STA $8B
	LDA $00B9F6,x
	STA $8C
	BRA .FinishDecomp2
.ExGFX100toFFF
	SBC #$0100
	STA $8A
	ASL
	CLC
	ADC $8A
	TAY
	LDA $0FF873
	STA $06
	LDA $0FF874
	STA $07
.FinishDecomp
	LDA [$06],y
	STA $8A
	INC $06
	BNE .NoCrossBank
	SEP #$20
	INC $08
	REP #$20
.NoCrossBank
	LDA [$06],y
	STA $8B
.FinishDecomp2
	LDA.w #!DecompBuffer
	STA $00
	SEP #$20
	LDA.b #!DecompBuffer>>16
	STA $02
	PHK
	PEA .Ret1-1
	PEA $84CE
	JML $00B8DC
.Ret1
	PLP
	PLX
	PLY
	RTS

UploadVRAMData:
	PHY
	PHP
	SEP #$10
	LDY #$80
	STY $2115
	STA $2116
	LDA #$1801
	STA $4300
	LDA.w #!DecompBuffer
	STA $4302
	LDY.b #!DecompBuffer>>16
	STY $4304
	LDA $8D
	STA $4305
	LDY #$01
	STY $420B
	PLP
	PLY
	RTS


I would actually like people to test this routine if they can. It seemed to work fine when I tested it, with the proper settings (make sure to have one of the alternate decompression routines enabled), but I'm not sure. It's not really worth making a patch out of...I was going to, but then I found out it could be done just with uberASM.

----------------

I'm working on a hack! Check it out here. Progress: 64/95 levels.
You don't need that huge routine for decompressing GFXs anymore, just set $00-$02 to output buffer, set A to GFX number in 16-bit mode and JSL $0FF900, like:

Code
STZ $00
REP #$20
LDA #$7EAD	; buffer = $7EAD00
STA $01
LDA #$0080	; deoompress ExGFX80.bin
JSL $0FF900


[do something with your gfx here]
GitHub - Twitter - YouTube - SnesLab Discord
Well, NMI isn't disabled during game mode 13, and since $0FF900 uses scratch RAM that is also used by NMI (why? Why, FuSoYa?!), it can't really be used here without the (small, but possible) risk of something exploding.

----------------

I'm working on a hack! Check it out here. Progress: 64/95 levels.
Originally posted by imamelia
(And if you're wondering why I didn't just use $7EAD00 then, it's because Lunar Magic—rather foolishly—also uses that space for ExAnimation data, so using it as a decompression buffer after that gets loaded will cause any animation in that level that uses the extra animated tile space to glitch up.)


There's nothing foolish about conserving RAM. Further, the game already does the same thing for the overworld's animated tiles.

Originally posted by imamelia
Well, NMI isn't disabled during game mode 13, and since $0FF900 uses scratch RAM that is also used by NMI (why? Why, FuSoYa?!), it can't really be used here without the (small, but possible) risk of something exploding.


Imamelia... $0FF900 uses the exact same RAM as the code you just posted. #ab{:P} A little less in fact, since LM's doesn't need to use $06.
Originally posted by FuSoYa
Originally posted by imamelia
(And if you're wondering why I didn't just use $7EAD00 then, it's because Lunar Magic—rather foolishly—also uses that space for ExAnimation data, so using it as a decompression buffer after that gets loaded will cause any animation in that level that uses the extra animated tile space to glitch up.)


There's nothing foolish about conserving RAM. Further, the game already does the same thing for the overworld's animated tiles.

Well, it's a good idea in theory, but it means you can't use the buffer while a level is running without screwing up ExAnimation, unless that level just uses the original GFX or the uncompressed GFX files (which, admittedly, are usually what I use). (Incidentally, on the subject of the overworld, I wonder if some of those tables could be moved to $7FC800 or something...)

Originally posted by FuSoYa
Originally posted by imamelia
Well, NMI isn't disabled during game mode 13, and since $0FF900 uses scratch RAM that is also used by NMI (why? Why, FuSoYa?!), it can't really be used here without the (small, but possible) risk of something exploding.


Imamelia... $0FF900 uses the exact same RAM as the code you just posted. #ab{:P} A little less in fact, since LM's doesn't need to use $06.

...Huh. That's true. I think the code I posted originally came from one of edit1754's patches...I wonder why he didn't just use $0FF900, then? I really should try to push that routine to its limit to see if I can figure out how far it will go before breaking when NMI hits. Or better yet, just make interrupts preserve everything properly. I vaguely recall someone making a patch to rewrite NMI to make it more efficient...maybe that would be worth looking into again? And actually, now that I think about it, does Lunar Magic have any such code for uploading the decompressed data to VRAM as well?

----------------

I'm working on a hack! Check it out here. Progress: 64/95 levels.
Originally posted by imamelia
Well, it's a good idea in theory, but it means you can't use the buffer while a level is running without screwing up ExAnimation, unless that level just uses the original GFX or the uncompressed GFX files (which, admittedly, are usually what I use).


I think you'll find that the number of people that use a decompressed ExAnimation file vastly outnumber the ones that want to do their own decompression outside of level/overworld load. And when you want to do both, the amount of RAM used is the same. So better that the ExAnimation file is there than taking up other free RAM elsewhere.

As usual, you have to look beyond your own case to determine whether something is really a good idea or not.

Originally posted by imamelia
(Incidentally, on the subject of the overworld, I wonder if some of those tables could be moved to $7FC800 or something...)


The first part of that is used by the overworld to store layer 1. Beyond it might be free though.

Originally posted by imamelia
I wonder why he didn't just use $0FF900, then?


His patch was originally made before LM 1.70, which was when LM made it's own routine more accessible.

Originally posted by imamelia
I really should try to push that routine to its limit to see if I can figure out how far it will go before breaking when NMI hits. Or better yet, just make interrupts preserve everything properly. I vaguely recall someone making a patch to rewrite NMI to make it more efficient...maybe that would be worth looking into again?


You may find this one useful: http://www.smwcentral.net/?p=section&a=details&id=5775

Although if you have time, it might be worth looking through NMI to locate the game code that's using scratch RAM when the current game frame hasn't finished processing (as fixing it directly may be more compatible with other patches than editing code near the start of the NMI routine like the patch above). It's something I had thought of looking into before, but it's not an issue I've run into myself so it wasn't much of a priority.

Originally posted by imamelia
And actually, now that I think about it, does Lunar Magic have any such code for uploading the decompressed data to VRAM as well?


Nothing meant for general use.
Originally posted by FuSoYa
Although if you have time, it might be worth looking through NMI to locate the game code that's using scratch RAM when the current game frame hasn't finished processing (as fixing it directly may be more compatible with other patches than editing code near the start of the NMI routine like the patch above). It's something I had thought of looking into before, but it's not an issue I've run into myself so it wasn't much of a priority.

From what I can tell, there are multiple such routines. $00A488 uses them, but so does $00A390, and there might be others.

----------------

I'm working on a hack! Check it out here. Progress: 64/95 levels.
Originally posted by imamelia
but so does $00A390


...eh? That's the tile animation DMA code. Remember you only want to look for NMI code that will run if $10 is non-zero. When it's 0 it should be fine for NMI to use scratch RAM, as it means the game is done processing the current frame and is waiting in a loop for NMI to do its thing.