16bit table indexing problem
Moderator: Moderators
Forum rules
- For making cartridges of your Super NES games, see Reproduction.
- Drew Sebastino
- Formerly Espozo
- Posts: 3496
- Joined: Mon Sep 15, 2014 4:35 pm
- Location: Richmond, Virginia
Re: 16bit table indexing problem
How would JSR affect the stack and why does it even use the stack? Does it store the address it jumps back to?
- Drew Sebastino
- Formerly Espozo
- Posts: 3496
- Joined: Mon Sep 15, 2014 4:35 pm
- Location: Richmond, Virginia
Re: 16bit table indexing problem
I know I'm not "thinking for myself" but what should I do differently now?
Re: 16bit table indexing problem
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.
- Drew Sebastino
- Formerly Espozo
- Posts: 3496
- Joined: Mon Sep 15, 2014 4:35 pm
- Location: Richmond, Virginia
Re: 16bit table indexing problem
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.): And this is the code for the metasprite drawing routine: And this is the result...(The desired outcome is on my earlier post.): 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...
This is the code for the main file (TempX and TempY are where X and Y are being stored.): And this is the code for the metasprite drawing routine: And this is the result...(The desired outcome is on my earlier post.): 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...
Re: 16bit table indexing problem
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).
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.
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: Select all
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.
;
- Drew Sebastino
- Formerly Espozo
- Posts: 3496
- Joined: Mon Sep 15, 2014 4:35 pm
- Location: Richmond, Virginia
Re: 16bit table indexing problem
(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? 
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.)
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.
Re: 16bit table indexing problem
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:
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.
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: Select all
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
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.
Re: 16bit table indexing problem
Which part was wrong?koitsu wrote:Movax12 is partially right and wrong.
- Drew Sebastino
- Formerly Espozo
- Posts: 3496
- Joined: Mon Sep 15, 2014 4:35 pm
- Location: Richmond, Virginia
Re: 16bit table indexing problem
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.)
Re: 16bit table indexing problem
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.Movax12 wrote:Which part was wrong?koitsu wrote:Movax12 is partially right and wrong.
Re: 16bit table indexing problem
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).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.)
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.
Re: 16bit table indexing problem
I mentioned the PHP+PLX pair as well.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.
-
psycopathicteen
- Posts: 3001
- Joined: Wed May 19, 2010 6:12 pm
Re: 16bit table indexing problem
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: Select all
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
Re: 16bit table indexing problem
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.)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.
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.