It is currently Tue Nov 21, 2017 7:02 pm

All times are UTC - 7 hours



Forum rules


Related:



Post new topic Reply to topic  [ 21 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Tue Sep 12, 2017 4:54 pm 
Offline
User avatar

Joined: Tue Sep 12, 2017 4:16 pm
Posts: 7
So I'm new to snes homebrew but I know how to program somewhat well.

So anyway, I've been sort of crapping around with SNES code in order to build my understanding of how this works and possibly eventually make my own game for lulz (Flappy Bird, anyone?)
There's a lot of helpful stuff on the superfamicom.org wiki to read about which has made it a lot easier for me.
Anyway, one thing I'm finding it especially difficult to grasp an understanding of is how you can display sprites on the screen. For example, how would I add just a stationary sprite to, say, the "walker" demo found in Neviksti’s Snes Starterkit?
So I make a borderless 32x32 pcx image and then run it through PCX2SNES and then get the .clr and .pic files.
I then know I'm supposed to add it to the bottom of my .asm file like so:
Code:
BikerSprite
   .INCBIN ".\\Pictures\\biker.pic"
        .INCBIN ".\\Pictures\\biker.clr"

I understand that I'm then probably supposed to use LoadBlockToVRAM. In the walker example, adding "LoadBlockToVRAM newsprite, $9000, $0800" will look like this:
Code:
        ;Load Tile and Character data to VRAM
   LoadBlockToVRAM   BackgroundMap, $0000, $2000   ; 64x64 tiles = 4096 words = 8192 bytes
   LoadBlockToVRAM   BackgroundPics, $2000, $6000   ; 384 tiles * (8bit color)= 0x6000 bytes
   LoadBlockToVRAM   ASCIITiles, $5000, $0800   ;128 tiles * (2bit color = 2 planes) --> 2048 bytes
   LoadBlockToVRAM   SpriteTiles, $6000, $2800   ;20 32x32 tiles * (4bit color = 4 planes) --> 10240 bytes
   LoadBlockToVRAM      BikerSprite, $9000, $0800; new test sprite


After spending a day or so studying the code, looking at other relevant code in other examples and reading stuff on superfamicom wiki, I can't really understand what exactly to do to just display my sprite stationary on the screen somewhere.
I know there are sprite numbers, but how do I assign a number? In the walker example, the main sprite appears to be put onto the screen using the following code:

Code:
   ;Set the priority bit of all the BG2 tiles
   LDA #$80
   STA $2115      ;set up the VRAM so we can write just to the high byte
   LDX #$5800
   STX $2116
   LDX #$0400      ;32x32 tiles = 1024
   LDA #$20
Next_tile:
   STA $2119
   DEX
   BNE Next_tile
   LDA #01
   STA $1E
   LDA #00
   STA $20
   
   JSR SpriteInit   ;setup the sprite buffer
   JSR JoyInit      ;setup joypads and enable NMI

   ;         ---12345678901234567890123456789012---

   ;setup our walking sprite
   ;put him in the center of the screen
   lda #($80-16)
   sta SpriteBuf1+sx
   lda #(224/2-16)
   sta SpriteBuf1+sy

   ;put sprite #0 on screen
   lda #$54
   sta SpriteBuf2

   ;set the sprite to the highest priority
   lda #$30         
   sta SpriteBuf1+spriority

   ;setup the video modes and such, then turn on the screen
   JSR SetupVideo   


It looks like the sprite number is #$54. If I change it to #$53 or 55, the walking guy sprite just disappears while I expected it to show a different sprite instead. Obviously there's more to it that I haven't been able to figure out by studying or trial and error. Can anyone help me with this?

My goal is to just get any sprite to appear in the walker example just so I can understand what exactly has to be done to do this again in a game of my own.

Thanks in advance


Top
 Profile  
 
PostPosted: Tue Sep 12, 2017 5:21 pm 
Offline
User avatar

Joined: Tue Apr 05, 2016 5:25 pm
Posts: 146
I suggest studying the OAM layout - you can find it here: https://wiki.superfamicom.org/snes/show/Sprites

In short, you should have a section of RAM that represents what you want the contents of OAM to be, and then during NMI/Vblank (or force blank, where the display is turned off) you upload that RAM copy into the actual OAM.

There's actually two tables. The first is 512 bytes and each sprite gets four bytes. This is where you select the sprite tile to use, X and Y position, and most attributes. The second is 32 bytes (called the OAM high table) and represents four sprites per byte. You'll need this to set the sprite size and allow it to go partway off the left side of the screen.

By the way, the register at $2101 is how you tell the PPU where in VRAM your sprite tiles are stored.

Edit: By sprite numbers do you mean the OAM index, or the sprite tile? The sprite tile can be considered as the Nth tile into a 128x128 graphic region of sprite graphics. As for the OAM index, that is up to you.

_________________
SNES NTSC 2/1/3 1CHIP | serial number UN318588627


Top
 Profile  
 
PostPosted: Wed Sep 13, 2017 5:31 am 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 1825
Location: DIGDUG
I'm probably going to quote this wrong, but my memory says.

The OAM low table isn't updated until the OAM high table is written to.

So basically, you need to write to both tables every frame, low and high.

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Last edited by dougeff on Wed Sep 13, 2017 3:38 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Wed Sep 13, 2017 11:35 am 
Offline

Joined: Fri Jul 04, 2014 9:31 pm
Posts: 801
Pretty sure that's wrong. The low table is behind a word buffer, which can have funky results if you're mixing reads and writes. The high table is byte access. They can be written independently.

What you describe would require a half-kilobyte write buffer...


Top
 Profile  
 
PostPosted: Wed Sep 13, 2017 1:14 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 1825
Location: DIGDUG
How am I to interpret this?

"Writes to the low table go into a word-sized buffer, which is written to the appropriate word of OAM when the high byte of the word is written" (from the link above at wiki.superfamicom.org)

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Wed Sep 13, 2017 1:24 pm 
Offline

Joined: Sun Mar 27, 2011 10:49 am
Posts: 206
Location: NYC
IIRC (haven't touched the SNES in a bit):

All writes go through a byte-sized port ($2104). When writing a word to the low table:

1. You write the low byte. It gets stored in a buffer.
2. You write the high byte. Only then is it written into the table.

The low byte/high byte thing going on there refers to a word for the low table; it has nothing to do with the high table.


Top
 Profile  
 
PostPosted: Wed Sep 13, 2017 1:28 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 1825
Location: DIGDUG
Got it, thanks.

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Wed Sep 13, 2017 3:31 pm 
Offline
User avatar

Joined: Tue Apr 05, 2016 5:25 pm
Posts: 146
For a second I thought I could gain 512 bytes of DMA headroom by just writing to the OAM transfer registers mid-frame. Heh, guess that's not the case.

_________________
SNES NTSC 2/1/3 1CHIP | serial number UN318588627


Top
 Profile  
 
PostPosted: Wed Sep 13, 2017 5:15 pm 
Offline

Joined: Wed May 19, 2010 6:12 pm
Posts: 2361
If you're not using all 8-channels for HDMA, you can set up a DMA channel for OAM before vblank.


Top
 Profile  
 
PostPosted: Wed Sep 13, 2017 5:50 pm 
Offline
User avatar

Joined: Tue Apr 05, 2016 5:25 pm
Posts: 146
By writing to the registers mid-frame, I didn't mean that I wanted sprite changes to take effect mid-frame. dougeff's post implied that you could upload OAM whenever, but be able to control when the newly uploaded OAM table is applied, so you'd only upload the 32-byte high table in the NMI routine and it'd work the same but you'd be doing it faster.

_________________
SNES NTSC 2/1/3 1CHIP | serial number UN318588627


Top
 Profile  
 
PostPosted: Wed Sep 13, 2017 5:58 pm 
Offline
User avatar

Joined: Tue Sep 12, 2017 4:16 pm
Posts: 7
HihiDanni wrote:
I suggest studying the OAM layout - you can find it here: https://wiki.superfamicom.org/snes/show/Sprites

In short, you should have a section of RAM that represents what you want the contents of OAM to be, and then during NMI/Vblank (or force blank, where the display is turned off) you upload that RAM copy into the actual OAM.


I understand that much. There's a low byte and a high byte. These contain data for the table having to do with telling the system how to use my sprite. As seen on the given link, it shows the record format for the low table and the high table and what the values are. But I can't find where they did this in the walker example.

By random guessing it looks like in the walker example, this is how they assign the high table, using the address you said:
Code:
      lda #$A3      ;Sprites 32x32 or 64x64, character data at $6000 (word address)
      sta $2101 


So A3 converted from hex to binary is 10100011 or 163 in decimal. It's supposed to contain bits from the first byte of the low table? Right? According to this link this means that the sprite size is 101 which is 32x32 or 64x64 which makes sense. Not sure how to interpret the 011 at the end. Its "base selection bits" which is 3. Hmm. The sprite in question starts at $6000 and is #$2800 in size so that can't be it. How does register 2101 tell the PPU where the sprites are stores again? I don't understand.

So in the current code, the OAM address for the sprite I presume is being added is 0, So I would redo that whole bit of code to add a second sprite except set 2102 and 2103 to 1 instead of 0 for sprite ID 1? But where is the part that says where the VRAM address and range is?
Here is all the code I have at vblank and below:
Code:
.DEFINE FrameNum $12      

VBlank:
   rep #$30      ;A/Mem=16bits, X/Y=16bits
   phb
   pha
   phx
   phy
   phd

   sep #$20      ; mem/A = 8 bit, X/Y = 16 bit

   ;*********transfer sprite data

   stz $2102      ; set OAM address to 0
   stz $2103

   LDY #$0400
   STY $4300      ; CPU -> PPU, auto increment, write 1 reg, $2104 (OAM data write)
   LDY #$0400
   STY $4302      ; source offset
   LDY #$0220
   STY $4305      ; number of bytes to transfer
   LDA #$7E
   STA $4304      ; bank address = $7E  (work RAM)
   LDA #$01
   STA $420B      ;start DMA transfer

   ;*********transfer BG2 data
   LDA #$00
   STA $2115      ;set up VRAM write to write only the lower byte

   LDX #$5800
   STX $2116      ;set VRAM address to BG3 tile map

   LDY #$1800
   STY $4300      ; CPU -> PPU, auto increment, write 1 reg, $2118 (Lowbyte of VRAM write)
   LDY #$0000
   STY $4302      ; source offset
   LDY #$0400
   STY $4305      ; number of bytes to transfer
   LDA #$7F
   STA $4304      ; bank address = $7F  (work RAM)
   LDA #$01
   STA $420B      ;start DMA transfer

   ;update the map co-ordinates
   lda MapX
   sta $210D
   lda MapX+1
   sta $210D

   lda MapY
   sta $210E
   lda MapY+1
   sta $210E

   ;update the joypad data
   JSR GetInput

   lda $4210      ;clear NMI Flag

   REP #$30      ;A/Mem=16bits, X/Y=16bits
   
   inc FrameNum
   PLD
   PLY
   PLX
   PLA
   PLB
      RTI
    

;End of demo Main code

;============================================================================
; SetupVideo -- Set the video mode for the demo
;----------------------------------------------------------------------------
; In: None
;----------------------------------------------------------------------------
; Out: None
;----------------------------------------------------------------------------
SetupVideo:
   php

   rep #$10      ;A/mem = 8bit, X/Y=16bit
   sep #$20
     
   lda #$A3      ;Sprites 32x32 or 64x64, character data at $6000 (word address)
      sta $2101         

   lda #$04      ;Set video mode 4, 8x8 tiles (256 color BG1, 4 color BG2)
      sta $2105         

   lda #$03      ;Set BG1's Tile Map VRAM offset to $0000 (word address)
      sta $2107      ;   and the Tile Map size to 64 tiles x 64 tiles

   lda #$52      ;Set BG1's Character VRAM offset to $2000 (word address)
      sta $210B      ;Set BG2's Character VRAM offset to $5000 (word address)

   lda #$58      ;Set BG2's Tile Map VRAM offset to $5800 (word address)
      sta $2108      ;   and the Tile Map size to 32 tiles x 32 tiles

   lda #$13      ;Turn on BG1 and BG2 and Sprites
      sta $212C

      lda #$0F      ;Turn on screen, full brightness
      sta $2100      

   lda #$FF      ;Scroll BG2 down 1 pixel
   sta $2110
   sta $2110         

   plp
   rts

.ENDS


Where on here is there any evidence of setting up the low table or high tables like in the link you provided? Is there an example somewhere where someone has programmed 2 independent sprites that I could look at and contrast that vs this? I'm sure I would be able to figure it out on my own if I had one of those but until then I'm trying to decode how it's done from here.

But thanks everyone for the help so far I hope I'm eventually able to figure this out.


Top
 Profile  
 
PostPosted: Wed Sep 13, 2017 6:19 pm 
Offline
User avatar

Joined: Tue Apr 05, 2016 5:25 pm
Posts: 146
Xeraster wrote:
I understand that much. There's a low byte and a high byte. These contain data for the table having to do with telling the system how to use my sprite. As seen on the given link, it shows the record format for the low table and the high table and what the values are. But I can't find where they did this in the walker example.

Be careful not to confuse low byte and high byte with low table and high table. Those are two different concepts.

Xeraster wrote:
By random guessing it looks like in the walker example, this is how they assign the high table, using the address you said:
Code:
      lda #$A3      ;Sprites 32x32 or 64x64, character data at $6000 (word address)
      sta $2101 

This doesn't affect the OAM table, it's just telling the PPU where the sprite graphics are stored in VRAM, which is a separate section of memory from OAM.

Quote:
Its "base selection bits" which is 3. Hmm. The sprite in question starts at $6000

So you uploaded the sprite graphic to word address 0x6000? Be careful, as the VRAM address registers go by word address, not byte address. Be careful not to upload to the wrong region. But in this case if it's at word address 0x6000 then you have the base selection bits set correctly.

Now here's the thing: In the OAM data format, the third byte of form 'cccccccc' is a tile offset, not an address offset. So to display the sprite graphic you uploaded to word address 0x6000 you should use 0 for this byte.

Quote:
So in the current code, the OAM address for the sprite I presume is being added is 0, So I would redo that whole bit of code to add a second sprite except set 2102 and 2103 to 1 instead of 0 for sprite ID 1?

You don't need to do this by hand. The OAM DMA transfer will send all the sprites for you in one go.

From the snippet:

Code:
   stz $2102      ; set OAM address to 0
   stz $2103

   LDY #$0400
   STY $4300      ; CPU -> PPU, auto increment, write 1 reg, $2104 (OAM data write)
   LDY #$0400
   STY $4302      ; source offset
   LDY #$0220
   STY $4305      ; number of bytes to transfer
   LDA #$7E
   STA $4304      ; bank address = $7E  (work RAM)
   LDA #$01
   STA $420B      ;start DMA transfer

This does the actual DMA transfer to OAM. See the value that's getting stored in $4302? That's the address in main RAM of what gets sent to OAM. In this example it's hardcoded to 0x400. I'm not sure why. It's probably an error in the example. It should probably refer to the SpriteBuf variable label mentioned in previous code snippets. So the SpriteBuf is where you want to make your changes.

_________________
SNES NTSC 2/1/3 1CHIP | serial number UN318588627


Top
 Profile  
 
PostPosted: Sat Sep 16, 2017 3:03 pm 
Offline
User avatar

Joined: Tue Sep 12, 2017 4:16 pm
Posts: 7
HihiDanni wrote:
Xeraster wrote:
This does the actual DMA transfer to OAM. See the value that's getting stored in $4302? That's the address in main RAM of what gets sent to OAM. In this example it's hardcoded to 0x400. I'm not sure why. It's probably an error in the example. It should probably refer to the SpriteBuf variable label mentioned in previous code snippets. So the SpriteBuf is where you want to make your changes.


OOOOH that $0400 thing makes sense now, because in the SpriteInit section, SpriteBuf1 is defined as $0400, so it's the same. So in order to code a second sprite, I could use write the 4 bytes of data starting with $0404. But then I have to use the high table to write the next parts and it's 2 bits per sprite, just like you said in the very first thing you said when you first replied to this thread.
Now the first sprite writes #$54 to $0600 aka SpriteBuf2. For a second sprite, would I want to use #$55? Because in binary that's already 01010100. There's already 2 BG layers which logically I would expect to use up the first 2 bits. Then the 1 working sprite I already have which should be occupying bits 5 and 6 (0 for x coord 9th bit, and 1 for large sprite size), so #$55 would make it 01010101, giving my desired 2nd sprite 0 for bit 7 and 1 for bit 1. Right?

Also, once I do that, I should be able to go the part where it does the DMA transfers. But then instead of writing $0400 to registers $4300 and $4302, it would be $0404 and I would set the OAM address to 1 on registers $2102 and $2103. Is this a correct assumption?

I know I'm a noob but thanks for taking the time to help me so far.

EDIT: I'm trying to do that idea I just wrote about. It almost works except I still don't know how to use the sprite I put in the VRAM at $9000 AND it messes up the background into glitchy weirdness. BUT HEY, I got a second sprite to display. NOW I'm starting to get somewhere! (almost)

You did mention that the DMA OAM transfer is supposed to send all the sprites in one go. I couldn't get that to work (because I'm probably doing something wrong). So I just have 2 of those DMA transfer sprite data blocks. Here's what it looks like:
Code:
   lda #($80-16)
   sta SpriteBuf1+sx
   lda #(224/2-16)
   sta SpriteBuf1+sy ;these 4 lines were already here
   
   lda #($80-10)
   sta $404
   lda #(224/2-16)
   sta $405

   ;put sprite #0 on screen.
   lda #$54
   sta SpriteBuf2

   ;set the sprite to the highest priority
   lda #$30         
   sta SpriteBuf1+spriority
   lda #$00      
   sta $408
   lda #$30;
   sta $407

My second DMA transfer block that I just added under the first one.
Code:
;*********transfer sprite2 data

   lda #$01
   sta $2102      ; set OAM address to 0
   lda #$01
   sta $2103

   LDY #$404
   STY $4300      ; CPU -> PPU, auto increment, write 1 reg, $2104 (OAM data write)
   LDY #$404
   STY $4302      ; source offset
   LDY #$0220
   STY $4305      ; number of bytes to transfer
   LDA #$7E
   STA $4304      ; bank address = $7E  (work RAM)
   LDA #$01
   STA $420B      ;start DMA transfer


I'm getting closer, but what am I doing wrong now?


Top
 Profile  
 
PostPosted: Sat Sep 16, 2017 6:38 pm 
Offline
User avatar

Joined: Tue Apr 05, 2016 5:25 pm
Posts: 146
That DMA to OAM code is already there in the VBlank handler, so you don't need to write it again elsewhere. You'll notice in the comments that the number of bytes it transfers is 0x220, which is the full size of OAM, hence it transfers all sprites.

Since you mentioned that SpriteBuf1 is at 0x400 and SpriteBuf2 is at 0x600, then SpriteBuf1 must correspond to the low table and SpriteBuf2 the high table. While you're still getting the hang of the OAM format, I would suggest just playing with the low table for now and then learning the high table later. But for sprite number 0, you'd change the low table at the first four bytes in SpriteBuf1, and the first two bits (first byte) of SpriteBuf2. For sprite number 1, you'll change the low table at SpriteBuf1 + 4, and for the high table it's still the first byte of SpriteBuf2.

Now might be a good time to learn about how index addressing works. The idea is you set the X index register to, say, 4 (a four byte offset, or sprite number 1), and then use code like the following to do the store:

Code:
ldx #4 ; Byte offset of 4 into the OAM low table
lda #$80
sta SpriteBuf1, x ; Set sprite 1's X coordinate to 128

As for the screen glitching, you probably wrote to the region of VRAM currently being used for either the BG tile graphics ("char") or the BG tilemaps.

_________________
SNES NTSC 2/1/3 1CHIP | serial number UN318588627


Top
 Profile  
 
PostPosted: Sat Sep 16, 2017 11:04 pm 
Offline

Joined: Fri Jul 04, 2014 9:31 pm
Posts: 801
Xeraster wrote:
There's already 2 BG layers which logically I would expect to use up the first 2 bits.

No, the BG layers have nothing to do with OAM. It's all sprites. Remember, the S-PPU is fixed-function; you can't trade sprites for BG layers. You get 128 sprites no matter what. You can't even turn off sprites, except by moving them offscreen.

Count the entries. There are exactly 512 bytes in low OAM, four bytes for each sprite. There are exactly 32 bytes in high OAM, with four sprites to a byte. That's 128 sprites.

EDIT: Sorry if that sounded condescending. I'm feeling a bit harried right now for unrelated reasons, and I was trying to make the point as clear as possible.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 21 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 7 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group