It is currently Fri Dec 15, 2017 10:37 pm

All times are UTC - 7 hours



Forum rules


Related:



Post new topic Reply to topic  [ 132 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6 ... 9  Next
Author Message
PostPosted: Mon Jan 19, 2015 5:25 pm 
Online
User avatar

Joined: Mon Sep 15, 2014 4:35 pm
Posts: 3157
Location: Nacogdoches, Texas
How would JSR affect the stack and why does it even use the stack? Does it store the address it jumps back to?


Top
 Profile  
 
PostPosted: Mon Jan 19, 2015 5:26 pm 
Offline
User avatar

Joined: Sun Jan 02, 2011 11:50 am
Posts: 522
Exactly.


Top
 Profile  
 
PostPosted: Mon Jan 19, 2015 5:27 pm 
Online
User avatar

Joined: Mon Sep 15, 2014 4:35 pm
Posts: 3157
Location: Nacogdoches, Texas
I know I'm not "thinking for myself" but what should I do differently now?


Top
 Profile  
 
PostPosted: Mon Jan 19, 2015 5:29 pm 
Offline
User avatar

Joined: Sun Jan 02, 2011 11:50 am
Posts: 522
Don't use the stack to store your values, or use stack relative addressing to access those values and remember to fix the stack up when you are done.


Top
 Profile  
 
PostPosted: Mon Jan 19, 2015 6:31 pm 
Online
User avatar

Joined: Mon Sep 15, 2014 4:35 pm
Posts: 3157
Location: Nacogdoches, Texas
Because using the stack has pretty much been eliminated, I tried using registers like I tried originally, but I still can't get it to work.

This is the code for the main file (TempX and TempY are where X and Y are being stored.):
Attachment:
MetaspriteTest2.png
MetaspriteTest2.png [ 23.66 KiB | Viewed 1591 times ]

And this is the code for the metasprite drawing routine:
Attachment:
Metasprite2.png
Metasprite2.png [ 14.62 KiB | Viewed 1591 times ]

And this is the result...(The desired outcome is on my earlier post.):
Attachment:
Result....png
Result....png [ 19.64 KiB | Viewed 1591 times ]

The result is actually the exact same as skipping the metasprite routine, leading me to believe that it jumps to the metasprite routine, but the first number in the table somehow turns into a 0. Because of this, right when the metasprite routine starts, it ends, because I saw a 0, making it think it was done. (This is how I tell it how many sprites to draw for the metasprite.) The problem is (obviously) I don't know how it got 0...


Top
 Profile  
 
PostPosted: Mon Jan 19, 2015 8:38 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3192
Location: Mountain View, CA, USA
Movax12 is partially right and wrong, but I'll cover it all.

I was going to write up a very long explanation, but it would involve me converting all the assembly code into machine language (raw bytes) -- especially important to show you what JSR is doing under the hood (specifically how the value the CPU pushes onto the stack is the address of the last byte of the opcode).

The root problem has to do with stack operations and order-of-operation. You're not correctly keeping track of what was last pushed on the stack.

A real-time debugger would show you this -- you'd see "weird values" show up in the X and Y registers, followed by when your code hit the PLP and RTS, things freaking out horribly bad.

So let's go over the code and how it executes -- again, a listing here would be SUPER helpful. Let's assume S (stack pointer) is at $03FF when this code starts, and that both indexes and accumulator are 16-bit. Also assume MetaspriteTable happens to be at memory location $9150, just for the hell of it (you'll see why this is important in a moment).

Code:
  ldy #$00
  ldx #MetaspriteTable
  phy
  phx
  jsr start_metasprite
  php
  ;
  ; At this point, the following values are on the stack (in memory):
  ;
  ; $03FF = $00 (high byte of Y, from PHY)
  ; $03FE = $00 (low byte of Y, from PHY)
  ; $03FD = $91 (high byte of X, from PHX)
  ; $03FC = $50 (low byte of X, from PHX)
  ; $03FB = high byte of address of last byte of operand of jsr start_metasprite call -- can't tell you what this is without a listing
  ; $03FA = low byte of address of last byte of operand of jsr start_metasprite call -- can't tell you what this is without a listing
  ; $03F9 = whatever P was when PHP was called
  ;
  ; S should be $03F9 at this point too (if my memory serves me right).
  ;
  rep #$10
  sep #$20
  plx        ; BUG BUG BUG
  ply        ; BUG BUG BUG
  ;
  ; X low byte = whatever $03F9 contained -- this would be the value pushed by PHP
  ; X high byte = whatever $03FA contained -- see above
  ;
  ; Y low byte = whatever $03FB contained -- see above
  ; Y high byte = whatever $03FC contained -- this would be $50, low byte of X when PHX was called
  ...
  ...
  plp        ; BUG BUG BUG
  ;
  ; P = whatever $03FD contained -- this would be $91, high byte of X when PHX was called
  ;
  rts        ; BUG BUG BUG
  ;
  ; The PC (program counter, i.e. where the CPU is currently executing) is going to be set to $0001 here.
  ; The value comes from $03FE and $03FF.  On an RTS, the CPU also increments the address it pulled
  ; by one (this would be "the instruction at the location after the last byte of the operand" -- what I
  ; was describing above). $0000 + 1 = $0001.
  ;
  ; Program at this point goes utterly bonkers, trying to execute (as code) whatever values are at $0001
  ; and onward.
  ;

I think that's all the time I'm going to spend today on this. I think so far I've spent around 5-6 hours of my time just on this one thread. This is about the point where I would start discussing with someone financial reimbursement for time + training sessions.


Top
 Profile  
 
PostPosted: Mon Jan 19, 2015 8:48 pm 
Online
User avatar

Joined: Mon Sep 15, 2014 4:35 pm
Posts: 3157
Location: Nacogdoches, Texas
(You don't have to respond today, but...) I guess using the stack for this is generally a no no? Like I said, I did try it by loading the values in registers, but it didn't work either for some reason. (Granted, not nearly as bad.) Also, If you use SNES9X debugger, do you think you could point me to a tutorial or something that can teach you how to use it? :oops:

Mainly, what I gather is that instead of pulling
; $03FC = $50 (low byte of X, from PHX) and
; $03FD = $91 (high byte of X, from PHX) into X and pulling
; $03FE = $00 (low byte of Y, from PHY) and
; $03FF = $00 (high byte of Y, from PHY) into Y, it's pulling

; $03F9 = (whatever P was when PHP was called) and
; $03FA = (low byte of address of last byte of operand of jsr start_metasprite call) into X and pulling
; $03FB = (high byte of address of last byte of operand of jsr start_metasprite call) and
; $03FC = $50 (low byte of X, from PHX) into Y, effectively screwing everything everything up.
(Metasprite information is wrong, it's writing to the OAM table offset by whatever, and it's returning to a totally wrong location.)

It seems that the stack is a bit more finicky than I had originally thought. :? It seems easier to just load values into registers. (After all, you do have 128KB of RAM.)


Top
 Profile  
 
PostPosted: Mon Jan 19, 2015 9:52 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3192
Location: Mountain View, CA, USA
You can use temporary variables or the stack. Temporary variables tend to be easier to comprehend, and the SNES (IMO) has a lot of RAM. The choice is yours. There's no wrong way. I think at this point though using temporary variables in direct page/RAM would be easier for you.

The issue with the stack is that because of the use of a subroutine via JSR, the last 2 bytes pushed onto the stack are going to be the address of the last byte of the operand of the JSR, which isn't what you're wanting via PLX/PLY.

That said, your code still had a bug relating to doing PHP followed by a (16-bit) PLX, so that's responsible for one of two issues.

Movax12 mentioned stack-based addressing, which is something the 65816 has (6502/65c02 does not have this). I don't know what the exact syntax is in WLA DX for it, but the common syntax for addressing is n,s where n is an "offset" that starts at the active stack pointer (S) and works backwards -- it's kinda like indexing using n,x or n,y just "via the stack" (which works backwards due to the fact that S decrements, not increments). Stack-relative stuff consists of a 2-byte instruction, which means it can only address up to 256 bytes "backwards from S". It also does not modify the stack, so it's still your responsibility to pull data off (to keep from having a stack overflow) later. Some example code:

Code:
  rep #$20
  lda #$1234
  pha
  jsr myroutine
  ...
  ...
  pla      ; Pull previous data (PHA) off stack (keep stack overflow from happening)
loop:
  bra loop

myroutine:
  php
  rep #$10
  lda $4,s   ; Load 16-bit data pushed onto the stack via PHA earlier, into accumulator
             ; $3,s would be one of the bytes of the JSR address
             ; $2,s would be one of the bytes of the JSR address
             ; $1,s would be the value of P pushed via PHP
  ...
  plp
  rts

The thing to remember about stack-based addressing is that the "last byte pushed on the stack" would be $1,s, because S always points to the "next" available spot. You shouldn't ever do something like $0,s.

It's believed that the main reason this mode was implemented was solely for things like subroutines that mimic C or other languages where arguments passed to a function are pushed on to the stack prior to the function (subroutine) being used.

One downside to stack-based addressing (and this upset a lot of people at the time): the number of opcodes supporting this addressing mode are very few, and all relate to the accumulator. You thus cannot do something like ldx $2,s (there is no such opcode supporting that addressing mode).

But as I said near the start: I think right now, using temporary variables would keep things simpler for you. No judgement there whatsoever -- you're still learning, so it's perfectly fine. :-)

P.S. -- I myself have never used the stack-relative addressing mode. This is the first time I've actually taken the time to fully look at it and take a shot at some example code using it. Here be dragons.


Top
 Profile  
 
PostPosted: Tue Jan 20, 2015 12:25 am 
Offline
User avatar

Joined: Sun Jan 02, 2011 11:50 am
Posts: 522
koitsu wrote:
Movax12 is partially right and wrong.


Which part was wrong?


Top
 Profile  
 
PostPosted: Tue Jan 20, 2015 11:40 am 
Online
User avatar

Joined: Mon Sep 15, 2014 4:35 pm
Posts: 3157
Location: Nacogdoches, Texas
Just wondering because, like I said, the loading and storing approach didn't work, I wonder, how does the table actually get loaded into ram? The table is 7 bytes long, so does it fill the register and 7 after it with information? How can the processor even hold the table if it can only hold 16 bits? (I'm sure my way of thinking things is way off.)


Top
 Profile  
 
PostPosted: Tue Jan 20, 2015 12:01 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3192
Location: Mountain View, CA, USA
Movax12 wrote:
koitsu wrote:
Movax12 is partially right and wrong.


Which part was wrong?

The post implied that the use of JSR was "responsible" for the bug -- only partially true (the PHP + PLX bug is the other half). That's how I read it anyway.


Top
 Profile  
 
PostPosted: Tue Jan 20, 2015 12:05 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3192
Location: Mountain View, CA, USA
Espozo wrote:
Just wondering because, like I said, the loading and storing approach didn't work, I wonder, how does the table actually get loaded into ram? The table is 7 bytes long, so does it fill the register and 7 after it with information? How can the processor even hold the table if it can only hold 16 bits? (I'm sure my way of thinking things is way off.)

What do you think the loop in build_metasprite is for? I'm starting to get the impression someone gave you a bunch of code you don't understand (how it works). Did someone give you this code? If so, it might be better to discuss with them this type of question, since they're the author. This is exactly why taking code snippets from people + using them (without understanding them) is a Bad Practise(tm) (that's my opinion).

Regarding "tables of data" -- a series of bytes (8-bit values) is the same thing as a series of words (16-bit values). They're just bytes of data in memory. How they're accessed/used is up to the actual program/code (in real-time). I explained this in an earlier post, re: how you were incrementing indexes by 1 and then accessing the table + offset that way (i.e. a byte at a time), rather than a word at a time. (I still think that code is buggy/broken, but I am intentionally not fixing it for you because teaching you how to fix it yourself is more important)

The .db and .dw assembler directives simply tell the assembler "stick some raw data in right here". The only difference is that .dw allows for convenient storing of data in little endian format (low byte first, high byte second). E.g. .dw $1234 would be the same as .db $34,$12, assuming one is accessing the data via a 16-bit read in the code.


Top
 Profile  
 
PostPosted: Tue Jan 20, 2015 1:00 pm 
Offline
User avatar

Joined: Sun Jan 02, 2011 11:50 am
Posts: 522
koitsu wrote:
The post implied that the use of JSR was "responsible" for the bug -- only partially true (the PHP + PLX bug is the other half). That's how I read it anyway.


I mentioned the PHP+PLX pair as well. :P


Top
 Profile  
 
PostPosted: Tue Jan 20, 2015 3:54 pm 
Offline

Joined: Wed May 19, 2010 6:12 pm
Posts: 2429
When it goes into the "infinite loop" part, are the index registers in 16-bit mode? Also, I think "ldy #$00" might have issues with assemblers if it is used in 16-bit mode, since it is written as an 8-bit number. Better write it as "ldy #$0000" to make sure it assembles correctly.

Code:
InfiniteLoop:
wai

php  ;;I don't know what mode it was in, but this saves it on stack

rep #$20
ldy #$0000
ldx #MetaspriteTable
sty YTemp
stx XTemp
jsr start_metasprite

plp  ;;This returns to whatever mode it was, assuming that it is in the correct mode for the rest of the code


Top
 Profile  
 
PostPosted: Tue Jan 20, 2015 5:35 pm 
Offline

Joined: Fri Jul 04, 2014 9:31 pm
Posts: 818
psycopathicteen wrote:
When it goes into the "infinite loop" part, are the index registers in 16-bit mode? Also, I think "ldy #$00" might have issues with assemblers if it is used in 16-bit mode, since it is written as an 8-bit number. Better write it as "ldy #$0000" to make sure it assembles correctly.

I think WLA DX is probably smart enough to figure it out, but if it's not, I doubt that will fix it. I've had trouble with this sort of thing when using absolute addressing; lda $0001 assembles as "A5 01" because the assembler defaults to the smallest byte count capable of expressing the number. To get "AD 01 00", I had to specify the operand length as word, as recommended in the readme: lda $0001.w. I suspect lda $01.w would have worked too. (The length suffix can also be put on the opcode instead of the operand; e.g. lda.w - in some cases I found appending ".l" to the opcode to be the only way to get the assembler to use the full 24-bit value of a label.)

Now, I was using the version from 2003, and it's possible the thing is smarter now, but the readme on the website still recommends using ".b", ".w", or ".l" to resolve ambiguity. The example given is and with an immediate value, which implies that it's not just addresses that might need this treatment.


Last edited by 93143 on Tue Jan 20, 2015 6:11 pm, edited 1 time in total.

Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 132 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6 ... 9  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: Slashbunny 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