SNES game programing help

Discussion of hardware and software development for Super NES and Super Famicom. See the SNESdev wiki for more information.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES game programing help

Post by Drew Sebastino »

bazz wrote:just have a metasprite x,y
based on the metasprite's x,y
automatic calculations can be performed to derive the proper relative x/y coordinates of the sub-sprites.

For instance, in your 16x32 example, composed of 2 16x16 sprites.. call em sprite0 and sprite 1 in this example
metasprite x,y , call em mx,my
thus,
sprite0 x,y = (mx,my)
sprite1 x,y = (mx,my + 16)

metasprite's x,y is the "controller" coordinates which the subsprite coords are derived from. It's easy :)
That's what I was trying to say when I said "add or subtract the distance between the other sprite and load the value into the other sprite to keep the distance between them the same.", but I'm really not good with words. I actually made a 32x16 spaceship this way before I even knew what metasprite meant. I was getting myself confused for some reason because I was thinking of using a sprite's coordinates for other sprites, but I was saying that that really wouldn't work because all the sprites would be jumbled around from metasprites appearing or disappearing or growing or shrinking. Basically, I was thinking that I would have to load from OAM every frame to dictate where the metasprite would be, but I guess I can just have separate registers for each metasprites x and y courdinates.

The only thing left I wonder is how you would write out the positions for the sprites that make up each metasprite? I basically mean that when you are done with this,
bazz wrote:sprite0 x,y = (mx,my)
sprite1 x,y = (mx,my + 16)
wouldn't you have to load the results into a register that goes to OAM, or would you just send the results strait to OAM? It seems kind of tricky making it make sure that each set of x and y courdinates are 2 registers apart. Actually, you really probably don't need to worry because you would fill the gaps with other sprite information. It still seems difficult to transfer the data to hi oam, though.

I really just need to get ahold of psychopathicteen...
User avatar
MottZilla
Posts: 2837
Joined: Wed Dec 06, 2006 8:18 pm

Re: SNES game programing help

Post by MottZilla »

You don't store anything right to OAM. You should have a copy in WRAM and DMA that to OAM during your NMI routine. All of your sprite table/list building should be to a section in WRAM which allows you to build the list outside of VBlank and transfer it during VBlank, saving valuable vertical blanking time so you can transfer more data to the PPU rather than wasting it calculating something you could have done outside VBlank.

Lets say you have a simple Meta Sprite system that is like this.

Format:
Number of Sprites in Meta Sprite
Sprite X
Sprite Y
Sprite Attributes
(repeat till end of list)

So you'd have a function that you pass parameters for the X/Y location and the Meta Sprite list you want it to use. The X/Y you'll have to decide what that should mean. Depending on game logic you could have the X/Y being the center of an object or the upper left hand corner. You could do other positions but these are probably the most common. So what you'd do for each entry in the Meta Sprite list would just be to add the relative X/Y to the Meta Sprite's X/Y. You will need to do something about the X bit in the high table for allowing sprites to enter the left edge of the screen smoothly. And then if you use two sprite sizes there is that too.

But you see the basic idea is you're just making a data list of everything that needs to go to some location on screen, so you build your OAM list by having a function to take your parameters and copy and modify a meta sprite list into a OAM list. I probably went in a few circles trying to help with this.
User avatar
bazz
Posts: 476
Joined: Fri Sep 02, 2011 8:34 pm
Contact:

Re: SNES game programing help

Post by bazz »

Mott , yeah a couple circles but you explained the most critical part, having a copy of OAM in WRAM to DMA during vblank.

Sounds like Espozo just needs his psycho friend :lol:
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES game programing help

Post by Drew Sebastino »

bazz wrote:Sounds like Espozo just needs his psycho friend
:roll:
Format:
Number of Sprites in Meta Sprite
Sprite X
Sprite Y
Sprite Attributes
So it's just like how you would set up OAM, isn't it? Except for the number of sprites in metasprite, right? So if you wanted a metasprite made of two sprites, you would set it up like:

Number of Sprites in Meta Sprite (2)
Sprite X
Sprite Y
Sprite Attributes
(you wouldn't write anything here would you?)
Sprite X
Sprite Y
Sprite Attributes

The sprite attributes are just the character selection byte and the pallete/flip byte, right? Sorry, but would It be alright if someone gave a working example written in ASM? :oops:
User avatar
bazz
Posts: 476
Joined: Fri Sep 02, 2011 8:34 pm
Contact:

Re: SNES game programing help

Post by bazz »

Nah man, haven't we helped enough? We have jobs you know :P
No but in all seriousness, define your own format
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES game programing help

Post by Drew Sebastino »

lda MetaspriteX
bazz wrote:Nah man, haven't we helped enough? We have jobs you know
Do you not have Christmas break?

Anyway, I tried to make it myself and I had tried to use an incrementing pointer for it, but I couldn't figure out how to get it to work. This just codes for a simple 32x16 sprite. I made it so "MetaspriteX" and "MetaspriteY" make the sprite move.

Code: Select all

	ldy MetaspriteY
	sta SpriteBuf1+0
	sty SpriteBuf1+1
	lda #$00
	sta SpriteBuf1+2
	lda #$30
	sta SpriteBuf1+3
	lda MetaspriteX
	adc #$10
	sta SpriteBuf1+4
	sty SpriteBuf1+5
	lda #$00
	sta SpriteBuf1+6
	lda #$30	
	sta SpriteBuf1+7
I know this can be made way better somehow because it looks terribly inefficient.
psycopathicteen
Posts: 3197
Joined: Wed May 19, 2010 6:12 pm

Re: SNES game programing help

Post by psycopathicteen »

Code: Select all

build_metasprite:
ldx {sprite_number}		//x-index is oam pointer
ldy {metasprite}		//y-index is metasprite data pointer
beq finished_metasprite		//no metasprite if metasprite = 0

lda {x_position}
sec
sbc {bg1x}
sta {temp_x}			//calculate screen coordinate X

lda {y_position}
sec
sbc {bg1y}
sta {temp_y}			//calculate screen coordinate Y


stz {hflipmask}			//h-flipmask is $ffff if
lda {attributes}		//h-flip is set
bit #$4000
beq +
lda #$ffff
sta {hflipmask}
dec {temp_x}
+;

stz {vflipmask}			//v-flipmask is $ffff if
lda {attributes}		//v-flip is set
bit #$8000
beq +
lda #$ffff
sta {vflipmask}
dec {temp_y}
+;


metasprite_loop:
cpx #$0200
beq finished_metasprite		//exit loop if 128 sprites
lda $0000,y			//have been written
bne +				//if $0000,y is 0, exit loop

finished_metasprite:
stx {sprite_number}
rts

+;
lsr				//bit 1 of $0000,y is sprite size
sta {oam}+$0222,x		//store size bit at {oam}+546,x
inc				
asl #2				//half size = 4 if small
sta {temp}			//half size = 8 if big




lda $0002,y			//$0002,y is x displacement
eor {hflipmask}			//negate if h-flipped
clc
adc {temp_x}			//add screen coordinate X
sec
sbc {temp}			//subtract half size
cmp #$0100
bcc +				//if x < 256, display sprite
cmp #$fff0
bcs +				//if x > -16, display sprite
jmp next_sprite			//if not, don't
+;
sta {oam}+$0000,x		//store X at {oam}+0,x
and #$0100
sta {oam}+$0220,x		//store bit 9 at {oam}+544,x


lda $0004,y			//$0004,y is y displacement
eor {vflipmask}			//negate if v-flipped
clc
adc {temp_y}			//add screen coordinate Y
sec
sbc {temp}			//subtract half size
cmp #$00e0
bcc +				//if y < 224, display sprite
cmp #$fff0
bcs +				//if y > -16, display sprite
jmp next_sprite			//if not, don't
+;
sta {oam}+$0001,x		//store Y at {oam}+1,x


lda $0006,y			//$0006,y is attributes
eor {attributes}
sta {oam}+$0002,x		//attributes are stored at {oam}+2,x
inx #4				//move x-index to next oam entry

next_sprite:
tya
clc
adc #$0008
tay				//move y-index to next data entry
jmp metasprite_loop		//loop back
Last edited by psycopathicteen on Tue Dec 30, 2014 4:27 pm, edited 1 time in total.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES game programing help

Post by Drew Sebastino »

WOW! :shock: Did you make that all by yourself? Oh, and thank you! :mrgreen: (I almost forgot to say thanks I was so surprised when I saw this.) If I do ever make a game, (which is doubtful) I can put your name in the credits. (Only if you want me to of course.)
User avatar
MottZilla
Posts: 2837
Joined: Wed Dec 06, 2006 8:18 pm

Re: SNES game programing help

Post by MottZilla »

It's not that hard to do, you just have to plan out how you want to handle it.

In the earlier example I was giving of a meta sprite I was thinking a format like this:

0.Number of Sprite Cells
1.Relative X Position
2.Relative Y Position
3.Tile Number
4.Attribute Byte
repeat 1 through 4 till you reach number of cells specified in 0

Additionally you might want to have a 5th byte so you can choose a sprite size. I know it's not code, but the concept is important to understand. If you have code you don't understand and have a problem later, you're going to have a hard time figuring it out.

Some other things to consider for your game are if you want to support flipping meta sprites or if you'd rather store additional copies already flipped and just point to the correct data based on it's direction.
psycopathicteen
Posts: 3197
Joined: Wed May 19, 2010 6:12 pm

Re: SNES game programing help

Post by psycopathicteen »

The way I did it was in this format:

word 0: 0 = exit routine, 1 = small sprite, 2 = big sprite
word 1: x displacement
word 2: y displacement
word 3: name and attributes

I use 16-bit words because it is difficult to check bounds and finding the 9th x-bit, using 8 bits.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES game programing help

Post by Drew Sebastino »

I'm bumping again. (I'm sure everyone is sick of me by now.) I took a long break from actually doing anything and I figured I'd actually do something. I looked at you're code, psychopathicteen, but it was a bit too complex for me right now. I was just going to ask for help again, but don't worry, though, because I wrote out a small code that I'm pretty sure will work if a few things are done to it. The code does not edit attributes for sprites (I am going to add this later)

Code: Select all

_build_metasprite:
lda MetaspriteTable,x
beq _metasprite_done
inx
lda MetaspriteTable,x
clc
adc XPosition
sta (where?)
inx
lda MetaspriteTable,x
clc
adc YPosition
sta (where?)
inx
bra _build_metasprite

_metasprite_done:
rts
On the parts where I said sta (where?), I know I have to store them to an area in ram that acts as OAM, (I already have that set up) but I don't know how to keep it from going in the same spot. (I don't know why, but I thought I could do sta OAM+y, but this doesn't exist.) I also don't know how to make the code work for more than one metasprite, as it just points to a table that is set up for a particular metasprite. Although I will add on to this eventually, I have the table set up in a way to where its:

byte 1: If 0, no more sprites in metasprite. (exit loop) If 1, build sprite for metasprite
byte 2: x coordinate for current sprite (the code isn't currently set up for the 9th x bit)
byte 2: y coordinate for current sprite
(Keep Repeating)
tepples
Posts: 22915
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: SNES game programing help

Post by tepples »

Espozo wrote:(I don't know why, but I thought I could do sta OAM+y, but this doesn't exist.)
The aaaa,Y addressing mode exists for all Group 1 instructions (ORA, AND, EOR, ADC, STA, LDA, CMP, SBC) and for the unofficial read-modify-write Group 1+2 instructions (SLO, RLA, SRE, RRA, DCP, ISC).

A display list to be copied to OAM is kept in a 256-byte page in main RAM. This page is often called "shadow OAM". A lot of games put shadow OAM at $0200-$02FF; some instead use $0300-$03FF or $0700-$07FF. You fill the array during draw time, and then you copy it to the PPU using LDA #>shadowoam STA $4014 as the first thing during vertical blanking.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES game programing help

Post by Drew Sebastino »

tepples wrote:Espozo wrote:(I don't know why, but I thought I could do sta OAM+y, but this doesn't exist.)The aaaa,Y addressing mode exists for all Group 1 instructions (ORA, AND, EOR, ADC, STA, LDA, CMP, SBC) and for the unofficial read-modify-write Group 1+2 instructions (SLO, RLA, SRE, RRA, DCP, ISC).
Are you saying OAM,y is the same thing as OAM+y? I thought it only worked with tables. (Which I've never understood. There just data though, right? What I should probably be saying is that I don't understand indexing...) If OAM,y is exactly the same as OAM+y, then couldn't I write:

Code: Select all

_build_metasprite:
lda MetaspriteTable,x
beq _metasprite_done
inx
lda MetaspriteTable,x
clc
adc XPosition
sta OAM,y
inx
iny ;This exists, right? (It looks weird)
lda MetaspriteTable,x
clc
adc YPosition
sta OAM,y
inx
iny ;I'm also sure this segment could be made better.
iny
iny
bra _build_metasprite

_metasprite_done:
rts
tepples wrote:A display list to be copied to OAM is kept in a 256-byte page in main RAM. This page is often called "shadow OAM". A lot of games put shadow OAM at $0200-$02FF; some instead use $0300-$03FF or $0700-$07FF. You fill the array during draw time, and then you copy it to the PPU using LDA #>shadowoam STA $4014 as the first thing during vertical blanking.
Is this just the oam copy I was talking about? I still kind of want to know how to replace "MetaspriteTable" with anything I want to.
User avatar
bazz
Posts: 476
Joined: Fri Sep 02, 2011 8:34 pm
Contact:

Re: SNES game programing help

Post by bazz »

I'm just going to throw out the idea of using DP addressing for filling the OAM table.
psycopathicteen
Posts: 3197
Joined: Wed May 19, 2010 6:12 pm

Re: SNES game programing help

Post by psycopathicteen »

Code: Select all

ldy #$0000
ldx #MetaspriteTable
jsr _build_metasprite

Code: Select all

_build_metasprite:
lda $0000,x
beq _metasprite_done
inx
lda $0000,x
clc
adc XPosition
sta OAM,y
inx
iny ;This exists, right? (It looks weird)
lda $0000,x
clc
adc YPosition
sta OAM,y
inx
iny ;I'm also sure this segment could be made better.
iny
iny
bra _build_metasprite

_metasprite_done:
rts
Post Reply