8x16 and whatever else unreg wants to know

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

yesyesyall
Posts: 28
Joined: Sat Mar 05, 2011 3:17 pm
Location: Houston, Texas

Post by yesyesyall »

unregistered wrote:^ yesyesyall, sorry, I'm uncomfortable to meet with you. :oops:
cool! totally understand!
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Post by unregistered »

unregistered wrote:
tokumaru wrote:It may seem strange if you are doing it in a hardcoded way (manually setting up all the sprites of a particular object), but usually programmers implement a sprite system. For each possible frame of a character there is a list of the necessary sprites to draw it. It's just a series of .db or .dw statements that represent things like "this frame needs 4 sprites; the first one is at coordinates (4, 5) and uses tile $78; the second is at coordinates (12, 8) and uses tile $79 (...)". The coordinates are usually relative to the position of the object/character being drawn.
How do you make the coordinates relative to the position of the object/character being drawn?

Code: Select all

aY .byte 10 
aX .byte 20 
.db aY, $80, $00, aX, aY, $81, $00, aX +1
would this work?

Code: Select all

aY .equ 10
aX .equ 20
.db aY,$80,$00, aX, aY, $81, $00, aX +1
this would work but I don't understand how the coordinates would stay relative. Aren't aY and aX constants here?
unregistered wrote:
tokumaru wrote:This means that the sprite system can draw any frame of any character if you point it to the correct list.
Really happy to have found this, thank you. :)
User avatar
qbradq
Posts: 972
Joined: Wed Oct 15, 2008 11:50 am

Post by qbradq »

The positions you specify in the meta-sprite are relative to the object's origin. You make it relative to the object's position by adding the object's position to those values. If you game scrolls you will also need to subtract the scroll amounts.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

There is no automatic way to make the coordinates relative. This is not something the NES or the assembler will do for you, you'll have to code the logic for this yourself.

Every frame, instead of blindly copying the meta-sprite definitions directly to OAM, you'll do some processing with that data. Since you have the position of the object somewhere in RAM, you'll have to add the relative sprite coordinates to the object coordinates in order to find the final sprite coordinates, which you'll then write to OAM. Be careful with sprites going off screen, as you probably don't want pats of your characters wrapping to the opposite side of the screen.
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Post by unregistered »

Thank you qbradq and tokumaru, that makes sense; I'm stumbling on one part... how do you, or what is the best way you can, write to OAM? Right now i have one meta-sprite to display and move around the screen. Am i susposed to use lda and sta to write all the sprites to $0200?
Each frame during v blank it sends all of $0200 - $02ff to OAM through the OAM_DMA register. That's all I can think of... lda and sta to page 02.... is there a better method?
User avatar
qbradq
Posts: 972
Joined: Wed Oct 15, 2008 11:50 am

Post by qbradq »

Nope. That's how most games (homebrew and commercial alike) do it.

Also, you do not usually allocate slots within the OAM page to specific objects. Rather you loop over all of your objects each frame and lay down thier OAM data in the OAM page. That way when you need to start cycling your sprites (and you will, trust me), you can just change your starting location in the OAM page each frame.

But we'll cover sprite cycling when you get there :D
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Post by unregistered »

unregistered wrote:
unregistered wrote:
tokumaru wrote:This means that the sprite system can draw any frame of any character if you point it to the correct list.
How do you point it to the correct list? So far I have this

Code: Select all

;80 81       
;90 91       
;a0 a1       
;b0 b1   
        .db aY, $80, $00, aX, aY, $81, $00, aX+8,
		    aY+8, $90, $00, aX, aY+8, $91, $00, aX+8,
		    aY+16, $a0, $00, aX, aY+16, $a1, $00, aX+8,
			aY+24, $b0, $00, aX, aY+24, $b1, $00, aX+8
There's something i'm missing. :(
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

It'd go a little something like this (untested):

Code: Select all

hero_frame1:
  .db aY,    $80, $00, aX, aY,    $81, $00, aX+8
  .db aY+8,  $90, $00, aX, aY+8,  $91, $00, aX+8
  .db aY+16, $a0, $00, aX, aY+16, $a1, $00, aX+8
  .db aY+24, $b0, $00, aX, aY+24, $b1, $00, aX+8
hero_frame2:
  .db aY,    $82, $00, aX, aY,    $83, $00, aX+8
  .db aY+8,  $92, $00, aX, aY+8,  $93, $00, aX+8
  .db aY+16, $a2, $00, aX, aY+16, $a3, $00, aX+8
  .db aY+24, $b2, $00, aX, aY+24, $b3, $00, aX+8
hero_frame3:
  .db aY,    $84, $00, aX, aY,    $85, $00, aX+8
  .db aY+8,  $94, $00, aX, aY+8,  $95, $00, aX+8
  .db aY+16, $a4, $00, aX, aY+16, $a5, $00, aX+8
  .db aY+24, $b4, $00, aX, aY+24, $b5, $00, aX+8
hero_frame4:
  .db aY,    $86, $00, aX, aY,    $87, $00, aX+8
  .db aY+8,  $96, $00, aX, aY+8,  $97, $00, aX+8
  .db aY+16, $a6, $00, aX, aY+16, $a7, $00, aX+8
  .db aY+24, $b6, $00, aX, aY+24, $b7, $00, aX+8

; Operator < produces the low byte of an address
sprite_layouts_lo:
  .db <hero_frame1, <hero_frame2, <hero_frame3, <hero_frame4

; Operator > produces the high byte of an address
sprite_layouts_hi:
  .db >hero_frame1, >hero_frame2, >hero_frame3, >hero_frame4

; Number of hardware sprites in each layout
sprite_layouts_count:
  .db 8, 8, 8, 8

draw_sprite:
  ; t0-t2 are temporary zero page locations holding
  ; the address of a particular sprite layout and the
  ; number of sprites left to draw
  lda sprite_layouts_lo,x
  sta t0
  lda sprite_layouts_hi,x
  sta t0+1
  lda sprite_layouts_count,x
  sta t2
  ldy #0
  ; oamIndex is a variable tracking how far you've written
  ; into shadow OAM (customarily at $0200-$02FF)
  ldx oamIndex
@loop:
  ; If you have the address of an array in a zero page pointer,
  ; you use the (d),y addressing mode and increase Y to go
  ; to the next byte.
  lda (t0),y
  iny
  ; etc.
  dec t2
  bne @loop
  stx oamIndex
  rts

some_other_method:
  ldx #2
  jsr draw_sprite

EDIT: Fixed a typo.
Last edited by tepples on Mon Apr 04, 2011 9:36 am, edited 1 time in total.
User avatar
qbradq
Posts: 972
Joined: Wed Oct 15, 2008 11:50 am

Post by qbradq »

Yea, what Tepples said :D

I have added a wiki document about using pointer tables. I wrote it prior to seeing Tepples's reply so it uses different code, but there is more explanation.

Dang Tepples, you code quick :D
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

tepple's example is right. First you have to define the sprite data for all possible animation frames. Then you make a list of pointers to each of the frames, in order to give each of them an index. Finally, you make a function/subroutine that will receive a meta-sprite index and an object index and will render the requested frame for the requested object.

You can use registers X and Y to pass the indexes as parameters to the sprite drawing function. This function should add the meta-sprite coordinates to the object's coordinates (this is why the function needs to receive an object index) in order to find the final sprite coordinates, and, if those coordinates are within screen limits, write them to the OAM buffer (usually $0200-$02FF).
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Post by unregistered »

tepples wrote:It'd go a little something like this (untested):

Code: Select all

hero_frame1:
  .db aY,    $80, $00, aX, aY,    $81, $00, aX+8
  .db aY+8,  $90, $00, aX, aY+8,  $91, $00, aX+8
  .db aY+16, $a0, $00, aX, aY+16, $a1, $00, aX+8
  .db aY+24, $b0, $00, aX, aY+24, $b1, $00, aX+8
hero_frame2:
  .db aY,    $82, $00, aX, aY,    $83, $00, aX+8
  .db aY+8,  $92, $00, aX, aY+8,  $93, $00, aX+8
  .db aY+16, $a2, $00, aX, aY+16, $a3, $00, aX+8
  .db aY+24, $b2, $00, aX, aY+24, $b3, $00, aX+8
hero_frame3:
  .db aY,    $84, $00, aX, aY,    $85, $00, aX+8
  .db aY+8,  $94, $00, aX, aY+8,  $95, $00, aX+8
  .db aY+16, $a4, $00, aX, aY+16, $a5, $00, aX+8
  .db aY+24, $b4, $00, aX, aY+24, $b5, $00, aX+8
hero_frame4:
  .db aY,    $86, $00, aX, aY,    $87, $00, aX+8
  .db aY+8,  $96, $00, aX, aY+8,  $97, $00, aX+8
  .db aY+16, $a6, $00, aX, aY+16, $a7, $00, aX+8
  .db aY+24, $b6, $00, aX, aY+24, $b7, $00, aX+8

; Operator < produces the low byte of an address
sprite_layouts_lo:
  .db <hero_frame1, <hero_frame2, <hero_frame3, <hero_frame4

; Operator > produces the high byte of an address
sprite_layouts_hi:
  .db >hero_frame1, >hero_frame2, >hero_frame3, >hero_frame4

; Number of hardware sprites in each layout
sprite_layouts_count:
  .db 8, 8, 8, 8

draw_sprite:
  ; t0-t2 are temporary zero page locations holding
  ; the address of a particular sprite layout and the
  ; number of sprites left to draw
  lda sprite_layouts_lo,x
  sta t0
  lda sprite_layouts_hi,x
  sta t0+1
  lda sprite_layouts_count,x
  sta t2
  ldy #0
  ; oamIndex is a variable tracking how far you've written
  ; into shadow OAM (customarily at $0200-$02FF)
  ldx oamIndex
@loop:
  ; If you have the address of an array in a zero page pointer,
  ; you use the (d),y addressing mode and increase Y to go
  ; to the next byte.
  lda (t0),y
  iny
  ; etc.
  dec t2
  bne loop
  stx oamIndex
  rts

some_other_method:
  ldx #2
  jsr draw_sprite
:mrgreen: Thank you tepples! :D And thank you qbradq for your work... haven't really thought about it yet, sorry, tired. And thank you tokumaru! This is the second time, i think, you have explained step by step the first guy's help and that helps me so much too! :D Tomorrow is going to be so much fun! :D Good night.
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Post by unregistered »

tepples, this code rocks the house!! Thank you so much! :)
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Post by unregistered »

I get the error "daprg.asm(250): Branch out of range."

frome here:

Code: Select all

		draw_sprite:
		  ; t0-t2 are temporary zero page locations holding
		  ; the address of a particular sprite layout and the
		  ; number of sprites left to draw
		  lda sprite_layouts_lo, x
		  sta t0
		  lda sprite_layouts_hi, x
		  sta t0+1
		  lda sprite_layouts_count, x
		  sta t2
		  ldy #0
		  ; oamIndex is a variable tracking how far you've written
		  ; into shadow OAM (customarily at $0200-$02FF)
		  ldx oamIndex
		@loop:
		  ; If you have the address of an array in a zero page pointer,
		  ; you use the (d),y addressing mode and increase Y to go
		  ; to the next byte.
		  lda (t0), y
		  iny
		  ;ect.
		  dec t2
		  bne loop ;########(LINE NUMBER 250)
		  stx oamIndex
		  rts
		  
		  
		  
		  
        ; Set initial value of dx
        lda #$01
        sta dx
        rts

; Load palette into $3F00
load_palette:
So why is it out of range? The only help i could find was
in this thread
koitsu wrote:Branch instructions range from +127 to -128. It's just a signed 8-bit number.
I don't understand though. :(
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

The problem is my fault. I partially rewrote the code to reflect an apparent coding style of not using the .proc statement of ca65. So I changed loop: to @loop: but not bne loop to bne @loop. I have edited the sample code.
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Post by unregistered »

Ok, I didn't know if the local link needed an @ in

Code: Select all

bne @loop
because with the @ asm6 builds the rom with out error; but, now, when running it, Nintendulator stops and says "Bad opcode, CPU locked" and VirtuaNES stops and says "Executed an undefined order." I thought this was brought on by adding the @ to loop up there... the asm6 README.TXT doesn't have an example of that code. So, thank you for helping me learn that it's proper to use an @ in that code. :) It's ok tepples :)
I'm still lost. It confuses me that asm6 would assemble a "bad opcode"? :?
Last edited by unregistered on Mon Apr 04, 2011 12:56 pm, edited 1 time in total.
Post Reply