SNES Programing Help 2

Discussion of hardware and software development for Super NES and Super Famicom.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
Sik
Posts: 1589
Joined: Thu Aug 12, 2010 3:43 am

Re: SNES Programing Help 2

Post by Sik »

But then you create a lot of other problems in the code as you need to account for the wrapping.

It'd be probably better to store coordinates as 16-bit integer (no fractional part) and speeds as 8.8 fixed point, then when applying the momentum fake the subpixel accuracy through dithering over time.
psycopathicteen
Posts: 3001
Joined: Wed May 19, 2010 6:12 pm

Re: SNES Programing Help 2

Post by psycopathicteen »

I guess it depends how much continuity you want. If you're okay with objects resetting themselves, or doing one way scrolling, you could do it this way, but if you need to come back to objects later, then it doesn't work as well.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES Programing Help 2

Post by Drew Sebastino »

Okay, you know, I tried to start working on using direct page, but just after writing

Code: Select all

lda ObjectOffset    ;make sure you're in 16-bit A mode
tcd                           ;set direct page (D) register to address of current object slot
It glitched up, and I didn't even write lda.b for anything yet, so I don't get it. I have two files, one called Metasprite and Metasprite Bad. (The sprite flipping isn't completed yet, so it looks really weird, but that isn't the problem.) Metasprite Bad is obviously the one that causes it to get jacked up. (It works fine until a bullet gets spawned by pressing B.) Game is using Metasprite, while Game Bad is using Metasprite Bad. I really don't have a clue what the problem is, because I've never done anything with direct page. I'd say look at bullet in Objects, and see Metasprite Bad.
MetaspriteDemoKoitsu.rar
(305.75 KiB) Downloaded 130 times
Sik
Posts: 1589
Joined: Thu Aug 12, 2010 3:43 am

Re: SNES Programing Help 2

Post by Sik »

psycopathicteen wrote:I guess it depends how much continuity you want. If you're okay with objects resetting themselves, or doing one way scrolling, you could do it this way, but if you need to come back to objects later, then it doesn't work as well.
I was actually thinking more about what would happen when a moving object crosses a boundary (which will inevitably happen).
User avatar
Khaz
Posts: 314
Joined: Thu Dec 25, 2014 10:26 pm
Location: Canada

Re: SNES Programing Help 2

Post by Khaz »

Espozo wrote:Okay, you know, I tried to start working on using direct page, but just after writing

Code: Select all

lda ObjectOffset    ;make sure you're in 16-bit A mode
tcd                           ;set direct page (D) register to address of current object slot
It glitched up, and I didn't even write lda.b for anything yet, so I don't get it. I have two files, one called Metasprite and Metasprite Bad. (The sprite flipping isn't completed yet, so it looks really weird, but that isn't the problem.) Metasprite Bad is obviously the one that causes it to get jacked up. (It works fine until a bullet gets spawned by pressing B.) Game is using Metasprite, while Game Bad is using Metasprite Bad. I really don't have a clue what the problem is, because I've never done anything with direct page. I'd say look at bullet in Objects, and see Metasprite Bad.
MetaspriteDemoKoitsu.rar
Okay, I have to leave for the weekend here and I'm not sure I have either the time or motivation to go through your code and actually find your problem for you, but I do have some suggestions:

When you start using Direct Page for the first time, it will probably cause a bunch of problems if your assembler is anything like WLA. I don't know if that's the case, but with WLA, I had to add a ".b" on every single instruction referencing an address less than $0100, because WLA just assumed that a LDA $0050 meant LDA $50 from direct page. If you have ANY other instructions anywhere in your code that are being interpreted as a direct-page instruction incorrectly, they would have worked just fine before but will screw things up now that you start moving the direct page register. That might not be a problem for you in ca65.

So what to do? Very first idea is simply preserve the D register before and after your object routine. If you PHD at the very beginning of it, and PLD at the end, then whatever you do inside the routine the D register will be restored (presumably to 0000) by the end and stop screwing up the rest of your code. You can tighten this up to just one instruction for testing. Say,

phd
lda objectOffset
tcd
lda.b objectXPosition
pld

And then see if that one lda.b instruction was being carried out correctly. If you can get one to work you can get them all to work.

FFFFFFFFFFFF OH WAIT I JUST REMEMBERED doesn't ca65 have some kind of CRIPPLING bug with direct page where you have to compensate for how stupidly it interprets it? I completely forgot about that and I virtually guarantee there's your problem. I'm afraid you'll have to ask somebody who actually uses ca65 about that...
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: SNES Programing Help 2

Post by tepples »

In ca65, you can use direct page addressing for any instruction that supports it with the < (low byte) operator. You just have to watch out for instructions that don't support direct page addressing, such as d,y (direct page indexed with Y) with anything other than STX or LDX.

Code: Select all

  phd
  lda objectOffset
  tcd
  lda <objectXPosition
  pld
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES Programing Help 2

Post by Drew Sebastino »

tepples wrote:In ca65, you can use direct page addressing for any instruction that supports it with the < (low byte) operator. You just have to watch out for instructions that don't support direct page addressing, such as d,y (direct page indexed with Y) with anything other than STX or LDX.

Code: Select all

  phd
  lda objectOffset
  tcd
  lda <objectXPosition
  pld
Yeah, that fixed it. (I haven't actually tried using it yet, I'm just saying I put it in and the game acts normally.) The thing is though, every time I want to load something using direct page, I have to use phd and pld? Isn't that not very efficient? I look at the object's x position, y position, and the several tile numbers for each sprite on a loop, all using direct page, so that's a problem.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: SNES Programing Help 2

Post by tepples »

You only have to push and pull D if you're returning to a routine that expects D not to have changed. And even then, you only need to push at the start and pull at the end.

Code: Select all

  phd
  lda #0
loop:
  tcd
  ; OMITTED: bunch of stuff
  tdc
  clc
  adc #SIZEOF_OBJECT
  cmp #SIZEOF_OBJECT * MAX_OBJECTS
  bcc loop
  pld
Thus D is really just another indexing register. But if you're using D as the base pointer for an index into an array of structures in this manner, and you want to access variables in your actual zero page inside this loop, you'll need to make sure to use absolute mode (in ca65, a:some_label) or absolute long mode (f:some_label) for those variables, depending on where the data bank register B points.
93143
Posts: 1371
Joined: Fri Jul 04, 2014 9:31 pm

Re: SNES Programing Help 2

Post by 93143 »

Khaz wrote:doesn't ca65 have some kind of CRIPPLING bug with direct page
I don't know if it qualifies as either crippling or a bug, but you do apparently have to pay attention because the memory segment feature is 6502-centric and only knows about zero page.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES Programing Help 2

Post by Drew Sebastino »

Ugg, Good Lord, what's wrong now? I tried to make object x and y positions use direct page, but it appears direct page is being loaded from the wrong location or something, because the values are 0 when they shouldn't be. There is now Game Bad or anything, I just have it to where Metasprite the sfc file is the messed up ones. Bullets don't spawn for some reason. (The code pretty much follows the players in terms of how it's set up, so I have no idea.)
MetaspriteDemoKoitsu.rar
(291.67 KiB) Downloaded 108 times
(Random, but I can't help but find the opcode phd a little funny.)
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES Programing Help 2

Post by Drew Sebastino »

Wait, I looked at my code and it makes no sense. Just to get this straight, does direct page act exactly like x and y? I adjusted my code to where I wrote

Code: Select all

  lda ObjectOffset
  tcd
and did

Code: Select all

  adc <ObjectTable+YPosition
Later and it still doesn't work. (Man, why isn't there just a "z". :( ) I think I sort of know the reason only the player had worked and not the bullets though. The player is the first object.

Seriously, what is wrong with this?

Code: Select all

.setcpu "65816"
.smart

; See Game.asm
.importzp Empty
.importzp MetaspriteTableOffset
.importzp MetaspriteDirection
.importzp MetaspritePalette
.importzp MetaspriteCharacterOffset
.importzp MetaspriteCount
.importzp HFlipMask
.importzp VFlipMask
.importzp SpriteCount
.importzp ObjectOffset
.import SpriteBuf1
.import SpriteBuf3
.import MetaspriteTable
.import ObjectTable

Identity = 0
XPosition = 2
YPosition = 4
XVelocity = 6
YVelocity = 8
Atributes = 10

; Used by Game.asm
.export start_metasprite

.segment "CODE"

;
; start_metasprite
;
; Upon entry:
;   X register = offset into MetaspriteTable to start at
;   Y register = offset to start writing at in SpriteBuf1
; 
.proc start_metasprite
  phd
  rep #$30    ; A=16, X/Y=16
  ldy SpriteCount
  ldx MetaspriteTableOffset

  lda ObjectOffset
  tcd

  stz HFlipMask
  lda MetaspriteDirection
  bit #$0001
  beq metasprite_loop
  lda #$FFFF
  sta HFlipMask

metasprite_loop:
  cpy #$0200		    ; sees if all 128 sprites are used up
  beq done
  and #$00FF
  sta SpriteBuf1+1,y        ; Store sprite Y position in SpriteBuf1+1,y
  lda MetaspriteCount       ; If MetaspriteCount is zero, then we're done.  Otherwise we have
  beq done                  ; metasprites to iterate over and populate for DMA (see VBlank)


  lda Empty,x               ; 1st byte = sprite X position (value 0-255)
  eor HFlipMask
  clc
  adc <ObjectTable+XPosition
  cmp #256
  bcc sprite_x_not_out_of_bounds
  cmp #65504
  bcs sprite_x_not_out_of_bounds
  bra sprite_out_of_bounds

sprite_x_not_out_of_bounds:
  and #$01FF
  sta SpriteBuf3,y          ; Store sprite X position SpriteBuf1+y
  and #$00FF
  sta SpriteBuf1,y          ; Store sprite X position SpriteBuf1+y
  lda Empty+2,x             ; 1st byte = sprite X position (value 0-255)
  eor VFlipMask
  clc
  adc <ObjectTable+YPosition
  cmp #224
  bcc sprite_y_not_out_of_bounds
  cmp #65504
  bcs sprite_y_not_out_of_bounds
  bra sprite_out_of_bounds

sprite_y_not_out_of_bounds:
  sta SpriteBuf1+1,y
  lda Empty+4,x
  sta SpriteBuf3+2,y        ; Sprite size
  lda Empty+6,x
  clc
  adc MetaspritePalette
  clc
  adc MetaspriteCharacterOffset
  sta SpriteBuf1+2,y        ; Palette/Character word
  txa
  clc
  adc #$0008
  tax
  iny
  iny
  iny
  iny
  dec MetaspriteCount       ; Decrement MetaspriteCount by 1
  bra metasprite_loop       ; Back to the loop...

sprite_out_of_bounds:
  txa
  clc
  adc #$0008
  tax
  dec MetaspriteCount       ; Decrement MetaspriteCount by 1
  bra metasprite_loop       ; Back to the loop...

done:
  sty SpriteCount	    ; Says how many sprites have been made
  pld
  rts

.endproc
UnDisbeliever
Posts: 77
Joined: Mon Mar 02, 2015 1:11 am
Location: Australia (PAL)
Contact:

Re: SNES Programing Help 2

Post by UnDisbeliever »

Khaz wrote:FFFFFFFFFFFF OH WAIT I JUST REMEMBERED doesn't ca65 have some kind of CRIPPLING bug with direct page where you have to compensate for how stupidly it interprets it? I completely forgot about that and I virtually guarantee there's your problem. I'm afraid you'll have to ask somebody who actually uses ca65 about that...
ca65 uses 1 byte addressing (direct page) if either:
  • The address is a zeropage variable (in a .zeropage segment or marked zp with .globalzp or .importzp), or
  • .lobyte or < function is used, or
  • The variable is a constant and < $100, or
  • The variable is part of a struct and its address is < $100

What I do is define all of an object's variables in a struct

Header:

Code: Select all

.struct NpcStruct
    state .addr

    xPos  .res 3
    yPos  .res 3

    ; ... etc ...
.endstruct

N_NPCS = 12

.global npcs


.segment "SHADOW"
    npcs:   .res .sizeof(NpcStruct) * N_NPCS

And access the fields through the struct, because they resolve to a constant < $100 they are in DP.

Code: Select all

    ; DP = a NpcStruct object
    LDX NpcStruct::state

    LDA NpcStruct::xPos + 2
    BMI NegativeX

    ; ... etc ...

Also writing up a game loop

Code: Select all

ProcessNpcs:

    PHD
    REP #$30
.A16
.I16
    LDA #npcs

@Loop:
        TCD

        LDX NpcStruct::state
        BEQ @Skip

        ; code to process NPCs

@Skip:
        TDC
        ADD #.sizeof(NpcStruct)
        CMP #npcs + N_NPCS * .sizeof(NpcStruct)
        BLT @Loop

    PLD
    RTS
(NOTE: you cannot nest structs if you use this pattern)
Espozo wrote:Wait, I looked at my code and it makes no sense. Just to get this straight, does direct page act exactly like x and y? I adjusted my code to where I wrote

Code: Select all

  lda ObjectOffset
  tcd
and did

Code: Select all

  adc <ObjectTable+YPosition
Later and it still doesn't work. (Man, why isn't there just a "z". :( ) I think I sort of know the reason only the player had worked and not the bullets though. The player is the first object.

Seriously, what is wrong with this?
I think your problem is that your trying to access the Metasprite* variables (by .importzp) and the objects as a 1 byte address at the same time. When you set the DP register all 1 byte addresses will be added to DP to form the effective address.

Secondly, the value of ObjectTableis $0620 according to map.txt. Masking it to a 1 byte addresses will not work as you anticipate. What I think you are after is adc <YPosition after setting DP to #ObjectTable.

Lastly, by running the code through a trace logger it appears the value of ObjectOffset is 0, not $0620 (#ObjectTable) so there isn't any DP offsetting anyway.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES Programing Help 2

Post by Drew Sebastino »

UnDisbeliever wrote:I think your problem is that your trying to access the Metasprite* variables (by .importzp) and the objects as a 1 byte address at the same time. When you set the DP register all 1 byte addresses will be added to DP to form the effective address.
What? :| What do you mean by "When you set the DP register all 1 byte addresses will be added to DP to form the effective address."? What are the "1 byte addresses?" How do I fix this particular problem?
UnDisbeliever wrote:Secondly, the value of ObjectTableis $0620 according to map.txt. Masking it to a 1 byte addresses will not work as you anticipate. What I think you are after is adc <YPosition after setting DP to #ObjectTable.
Again, what?
UnDisbeliever wrote:Lastly, by running the code through a trace logger it appears the value of ObjectOffset is 0, not $0620 (#ObjectTable) so there isn't any DP offsetting anyway.
If you're looking at the first code, I severely jacked it up. #ObjectTable is the offset of where the object table starts, and ObjectOffset is the offset that says what object we're on in the object table. (Sorry if the names weren't very clear...) Should I do

Code: Select all

lda #ObjectTable
clc
adc ObjectOffset
tcd
and then just do something like

Code: Select all

adc <YPosition
Latter?

Edit: I tried what I just suggested, and it still doesn't work. :?
MetaspriteDemoKoitsu.rar
(291.7 KiB) Downloaded 122 times
I guess it still doesn't work because of problems #1 and #2 you said?
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: SNES Programing Help 2

Post by tepples »

Espozo wrote:
UnDisbeliever wrote:I think your problem is that your trying to access the Metasprite* variables (by .importzp) and the objects as a 1 byte address at the same time. When you set the DP register all 1 byte addresses will be added to DP to form the effective address.
What? :| What do you mean by "When you set the DP register all 1 byte addresses will be added to DP to form the effective address."? What are the "1 byte addresses?"
It might be easier to understand with a concrete example. Assume D = $0880 and the CPU is executing the instruction adc $23. The "1-byte address" is $23. The CPU adds the value 35 ($23) to the current value of D ($0880), producing $08A3, and then uses $0008A3 as the address of the operand of adc.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES Programing Help 2

Post by Drew Sebastino »

So what's wrong then? Isn't what I've done correct? XPosition is 2, and I am adding 2 to the table offset + the object's offset.
Post Reply