16bit table indexing problem

Discussion of hardware and software development for Super NES and Super Famicom. See the SNESdev wiki for more information.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: 16bit table indexing problem

Post by Drew Sebastino »

I really had no idea I was using an 8 bit accumulator. :oops: I really just don't understand what REP and SEP mean. I actually had this code working originally, (see SNES Programing Help) but I wanted to use 16bit values because it is apparently easier to use the 9th x bit. I tried this code now, but the screen now turns black and all memory seems to get erased. (I think this uses a 16bit accumulator.)

Code: Select all

.BANK 0 SLOT 0
.ORG 0
.SECTION "MetaspriteCode"

start_metasprite:
	php
	rep #$30
	sep #$10

_build_metasprite:
lda $0000,x
beq _metasprite_done
inx
lda $0000,x
clc
adc XPosition
and #$FF00          ;I switched it from #$00FF, to #$FF00
sta SpriteBuf1,y
inx
lda $0000,x
clc
adc YPosition
and #$FF00
sta SpriteBuf1+1,y
inx
iny
iny
iny
iny
bra _build_metasprite

_metasprite_done:
rts

metasprite_done:
	plp
	rts

.ENDS
Now the Table:

Code: Select all

MetaspriteTable:
	.DW $FFFF,$0000,$0000,$FFFF,$0010,$0000,$0000
I know that the upper byte of x and y is being erased, but it shouldn't cause the game to crash I don't think. (how would you keep the upper byte? having the 16 bit accumulator ready before you load the table? and go to the Metasprite routine?)

Also, the reason for the formatting of the code to not be consistent is because of copy and pasting. :P
Okay, that straightens that out, at least on my end. Sorry, Espozo.
No harm done. :)
And yes, I will probably end up switching to ca65, mostly because it has Super FX support.
Will you have to rewrite the code to make it work on ca65?
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: 16bit table indexing problem

Post by koitsu »

REP and SEP allow you to set and unset (adjust) the bits of P (processor status). REP stands for "REset P" (set bits to 0) and SEP stands for "SEt P" (set bits to 1). Any bit you set to 1 in the operand means "affect that bit". That should help explain why something like SEP #$20 sets bit 5 to 1 (8-bit accumulator), and why REP #$20 would set bit 5 to 0 (16-bit accumulator).

The bits in P are ones you already rely on heavily anyway, but the 65816 allows you to tweak those directly (rather than through explicit opcodes, e.g. CLC) if you wish. These are mostly compatible with the 6502 and 65c02, but not entirely:

Code: Select all

Bit 7 (n) = negative flag (1 = most-significant bit (MSB) set, 0 = MSB unset)
Bit 6 (v) = overflow flag (1 = two's complement error, 0 = two's complement OK)
Bit 5 (m) = accumulator register size flag (1 = 8-bit, 0 = 16-bit)
Bit 4 (x) = index register size flag (1 = 8-bit, 0 = 16-bit)
Bit 3 (d) = decimal mode flag (1 = BCD enabled, 0 = BCD disabled)
Bit 2 (i) = IRQ flag (1 = disable IRQ, 0 = enable IRQ)
Bit 1 (z) = zero flag (1 = last result was zero)
Bit 0 (c) = carry flag (1 = last result required carry)
The letters in parentheses are the common abbreviation for the individual bit in P. For the register size flags, in some emulators/tools, when they're capitalised it means the bit is unset (e.g. x means 8-bit, X means 16-bit). The capitalisation method, however, is not universal, but the letters are.

There's also the emulation bit (e) that isn't part of P (it's a separate bit), which is what defines if the CPU is in 65c02 emulation mode or not (e=0 means native 65816 mode, e=1 means 65c02 emulation mode). Please don't think about this though, you're doing real 65816 (thankfully) and don't get hung up on this paragraph.

Your question makes me wonder if you have a 65816 reference book at all. If not, I'd recommend getting the copy we keep of the one Western Design Center had up publicly for a while. It's at the bottom of this page, and contains actual real-world code examples with thorough explanations (not just in-line comments) of how things work, along with full opcode descriptions, charts, and so on:

http://wiki.nesdev.com/w/index.php/Programming_guide

Before WDC got their hands on it, it was a book created by Ron Lichty and David Eyes, and was the #1 resource in the 90s for 65816 programming (and works quite well for learning 6502/65c02 too, actually). I thankfully have a real paperback copy and have had it since I was a kid doing 65816. I've used it so much the cover has been torn off.

Anyway, I simply got in the habit of remembering the following "chart" by heart:

Code: Select all

REP #$10 = 16-bit indexes
REP #$20 = 16-bit accumulator
REP #$30 = 16-bit accumulator and 16-bit indexes
SEP #$10 = 8-bit indexes (upper top byte of X/Y lost/zeroed)
SEP #$20 = 8-bit accumulator
SEP #$30 = 8-bit accumulator and 8-bit indexes
You therefore cannot, in a single SEP or REP statement, do something like "use 8-bit accumulator and 16-bit indexes" -- it requires two opcodes given the nature of how the processor bits work and how SEP/REP work.

Screwing with any of the other bits of P directly through REP/SEP is considered "extremely uncommon" (don't let anyone tell you otherwise). For example: if you look at the InitSNES macro, for whatever reason someone did REP #$38, rather than CLD / REP #$30. They are the same thing, and whoever did that just wanted to save 2 cycles + 1 byte, even though it doesn't really matter (routine is only called once during reset). As I've stated in the past, I consider this kind of optimisation completely unnecessary and is often done by people (with the context in question kept in mind) just to "show off". I don't think it makes for good learning material.
Espozo wrote:I really had no idea I was using an 8 bit accumulator. :oops: I really just don't understand what REP and SEP mean. I actually had this code working originally, (see SNES Programing Help) but I wanted to use 16bit values because it is apparently easier to use the 9th x bit. I tried this code now, but the screen now turns black and all memory seems to get erased. (I think this uses a 16bit accumulator.)

Code: Select all

.BANK 0 SLOT 0
.ORG 0
.SECTION "MetaspriteCode"

start_metasprite:
	php
	rep #$30
	sep #$10
...
This code sets 16-bit accumulator and 16-bit indexes, then proceeds to set 8-bit indexes. This means you've lost the upper byte of the X/Y index registers, which is the entire problem I told you about.

Furthermore, using 16-bit accumulator, as I told you, would break your program/misbehave because of how it's written.

You have to change your actual code in build_metasprite, and the changes you did do are flat out incorrect -- you need to think about it a bit more (pay close attention to what you're doing with SpriteBuf1 and *WHERE* (offset-wise) within SpriteBuf1 you're writing data, how much data (in bytes) you're writing compared to previously, and how you're later using SpriteBuf1. All of that matters!). Pay close attention to how many times you're calling INX/INY (make sure that's correct vs. how you use those indexes both for reading and writing). And don't forget that YPosition and XPosition will probably need to increase in size as well (I think these are simply memory locations that are read, and so you'll need to make sure those are words not bytes). I feel I've spent enough time on this already, especially for one day. :P
I know that the upper byte of x and y is being erased, but it shouldn't cause the game to crash I don't think. (how would you keep the upper byte? having the 16 bit accumulator ready before you load the table? and go to the Metasprite routine?)
I explained how you "keep" the upper byte of X/Y when using REP/SEP, and why you do it a certain way. :/ If you really need to save this, then you need to store the 16-bit X value somewhere temporary (or push it onto the stack) before you change its size, and when you want to restore the value, you need to switch back to 16-bit indexes and then restore it (or pull it off the stack). Just don't forget that if you push a 16-bit value onto the stack, you need to make sure to pull it off later also as 16-bit, otherwise you'll have a stack underflow situation eventually.

Temporary variable method:

Code: Select all

  ;
  ; Assume 16-bit indexes here
  ;
  stx TempX  ; Store 16-bit X in TempX variable (should be in direct page or RAM, needs to be a word/2 bytes)
  sty TempY  ; Store 16-bit Y in TempY variable (should be in direct page or RAM, needs to be a word/2 bytes)
  sep #$10   ; 8-bit indexes -- top byte of X and Y are lost
  ;
  ; Your code here does something with 8-bit indexes...
  ;
  ; Now we're done and need to go back to 16-bit indexes and restore what X/Y were...
  ;
  rep #$10   ; 16-bit indexes
  ldy TempY  ; Load 16-bit Y with content of TempY variable
  ldx TempX  ; Load 16-bit X with content of TempX variable
Stack method:

Code: Select all

  ;
  ; Assume 16-bit indexes here
  ;
  phx       ; Push 16-bit X onto stack
  phy       ; Push 16-bit Y onto stack
  sep #$10  ; 8-bit indexes -- top byte of X and Y are lost
  ;
  ; Your code here does something with 8-bit indexes...
  ;
  ; Now we're done and need to go back to 16-bit indexes and restore what X/Y were...
  ;
  rep #$10  ; 16-bit indexes
  ply       ; Pull 16-bit value off stack and put into Y
  plx       ; Pull 16-bit value off stack and put into X
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: 16bit table indexing problem

Post by Drew Sebastino »

I was going to come back here complaining about how I tried the stack method on the metasprite routine and it didn't work but I just realized that I didn't pull in the reverse order pushed. Heh... (I've reverted back to the original 8 bit code I made, but I figured I could work on it a little more to prepare myself to make it 16 bit) The main reason I wanted to post here again is to ask if there is a way to view PDF files on Windows 8 not in the obnoxious full screen "app mode" (or whatever) because I kind of want to look at the SNES programming manual and try to program something without having to keep minimizing the window. (In my opinion, Windows 8 seems like a step back in many respects, but I know support for Windows 7 is eventually going to be dropped...)

This is what I mean:
Screenshot (140).png
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: 16bit table indexing problem

Post by lidnariq »

Install any non-"metro" PDF reader. Foxit seems to be popular.
("Metro" being the codename for the awful full-screen tablet windows 8 UI thing.)
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: 16bit table indexing problem

Post by Drew Sebastino »

Thank You! Windows 8 seems way more "tablet oriented". (Basically, make it stupidly easy to use so a kindergartener can play on it by stripping things features from it.)
User avatar
nicklausw
Posts: 376
Joined: Sat Jan 03, 2015 5:58 pm
Location: ...
Contact:

Re: 16bit table indexing problem

Post by nicklausw »

Espozo wrote:I honestly have no idea how to compile it. (If someone could post it already compiled in a rar, I would greatly appreciate it.)
Visual Studio and a bunch of other crap is needed. Don't use the binaries given on Ville's website, as those are somewhat outdated (and some bug fixes along with improvements have been made since then).

By the assumption that you're using Windows, here's my binaries. Fresh off GitHub. Made them last month as I recall.

EDIT: just remembered I also compiled them for Ubuntu, so I'll put those there aswell. Zip = windows, Tar = Ubuntu.
Attachments
WLA-DX.tar
(1.29 MiB) Downloaded 259 times
WLA-DX.zip
(922.77 KiB) Downloaded 245 times
Last edited by nicklausw on Mon Jan 19, 2015 3:16 pm, edited 1 time in total.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: 16bit table indexing problem

Post by Drew Sebastino »

Espozo wrote:I was going to come back here complaining about how I tried the stack method on the metasprite routine and it didn't work but I just realized that I didn't pull in the reverse order pushed. Heh...
Of course, I didn't even leave it on for a whole minute to see the game crash... I honestly have no clue what the problem is. I have a file in the folder named MetaspriteTest that works perfectly fine, and MetaspriteTest2 that crashes after a minute. (Sprites go all over the place, then VRAM gets totally wiped out and a whole bunch of nonsense in written to WRAM) The only difference between these two codes is that I wrote PHY and PHX before I jumped to Metasprite2 and then wrote PLX and PLY when I got there. I swear, do I have a brain disorder or something? Because I write something that makes perfect sense to me, it doesn't work in the slightest, and then someone fixes it in under half a minute when I've been staring at it for an hour.
Metasprite Demo.rar
(237.96 KiB) Downloaded 296 times
By the way, I haven't read all of the SNES programming manual (obviously), but I'm not sure how much it would help me with this particular problem.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: 16bit table indexing problem

Post by koitsu »

Have you spent time in an emulator's real-time debugger? This will almost certainly, 100%, tell you what is going on / why your code is "going crazy". Chances are you have a loop that is either never ending (and things are going off into lala land, address-wise) or you have some buggy code (duh).

I don't see any PHX/PHY/PLY/PLX code in these. And because of the naming convention + lack of good description of what you changed and where, it takes me a lot of time to sift through this to see what all you did that might be causing the problem. I'll try to focus on what looks like it may be a problem:

Code: Select all

Sprites.asm --

.DEFINE SpriteBuf1	$0400
.DEFINE SpriteBuf2	$0600

MetaspriteTest2.asm --

.DEFINE TempX		$1800
.DEFINE TempY		$1802
.DEFINE MapX		$1804
.DEFINE MapY		$1806

InfiniteLoop:

	WAI

	ldy #$00
	ldx #MetaspriteTable
	sty TempY
	stx TempX
	jsr start_metasprite

Metasprite2.asm --

start_metasprite:
	php
	sep #$10
	rep #$10
	ldy TempY
	ldx TempX

build_metasprite:
	lda $00,x
	beq metasprite_done
	inx
	lda $00,x
	sta SpriteBuf1,y
	inx
	lda $00,x
	sta SpriteBuf1+1,y
	iny
	inx
	iny
	iny
	iny
	bra build_metasprite

metasprite_done:
	plp
	rts

MetaspriteTable:
	.DB $01,$00,$00,$01,$10,$00,$00

1. Based on past conversation, I believe indexes to be 16-bit the time the LDX/LDY statements in InifiniteLoop are called. I'm just noting this here because it matters.

2. The SEP #$10/REP #$10 statement in the start_metasprite routine makes no sense -- you're setting 8-bit indexes, followed immediately by setting 16-bit indexes. That means I'm not sure what the accumulator size is at the time the later code is run, and I've covered why this matters with the assembler (since it tracks what the register sizes are as best as it can -- you could verify it if WLA DX wasn't a pile of junk with listing files... sigh...), but it also matters when your code is being run in real-time.

To me, based on the code in build_metasprite, it clearly looks like you want an 8-bit accumulator because you're doing stuff like this:

Code: Select all

	lda $00,x
	beq metasprite_done
	inx
	lda $00,x
	sta SpriteBuf1,y
	inx
	lda $00,x
	sta SpriteBuf1+1,y
Let's assume X=$8F00 and Y=$0000 when we enter this code, just for understanding what is going on here. Again, I'm discussing this because your goal is trying to convert a routine to use 16-bit values. The code then gets executed like this in real-time (see comments) -- I'm excluding the BRA statement because it's implied/unconditional:

Code: Select all

        lda $00,x               ; Load accumulator from $0000,x (effective address = $8F00)
        beq metasprite_done     ; If zero, branch -- value is not zero
        inx                     ; X = $0001
        lda $00,x               ; Load accumulator from $0000,x (effective address = $8F01)
        sta SpriteBuf1,y        ; Store at $0400,y (effective address = $0400)
        inx                     ; X = $0002
        lda $00,x               ; Load accumulator from $0000,x (effective address = $8F02)
        sta SpriteBuf1+1,y      ; Store at $0400+1,y (effective address = $0401)
        iny                     ; Y = $0001
        inx                     ; X = $0003
        iny                     ; Y = $0002
        iny                     ; Y = $0003
        iny                     ; Y = $0004

        lda $00,x               ; Load accumulator from $0000,x (effective address = $8F03)
        beq metasprite_done     ; If zero, branch -- value is not zero
        inx                     ; X = $0004
        lda $00,x               ; Load accumulator from $0000,x (effective address = $8F04)
        sta SpriteBuf1,y        ; Store at $0400,y (effective address = $0404)
        inx                     ; X = $0005
        lda $00,x               ; Load accumulator from $0000,x (effective address = $8F05)
        sta SpriteBuf1+1,y      ; Store at $0400+1,y (effective address = $0405)
        iny                     ; Y = $0005
        inx                     ; X = $0006
        iny                     ; Y = $0007
        iny                     ; Y = $0008
        iny                     ; Y = $0009
Do you now see -- when considering the offsets (effective addresses) discussed -- how/why 8-bit accumulator vs. 16-bit accumulator matters here? A 16-bit accumulator, during your ldx $00,x routine, is going to load two bytes from that address -- e.g. $8F00 and $8F01 in a single load. Yet your INX statements seem to indicate you're working with things at a byte level.

So here's the thing -- and I have not checked to see if this is the case (this is for you to do!): the wrong accumulator size could in fact account for this loop never ending and a lot of weird things going on (depends on what the NMI handler is doing). The only way the loop ends is if the accumulator, when loading from $0000,X, is zero. With a 16-bit accum that means the value has to be $0000, with an 8-bit accum that means the value has to be $00.

So let's take a look at what would happen with a 16-bit accumulator (again, not sure if this is the case! -- for you to figure out!), showing the actual values that are getting read/written. Again, assuming X=$8F00, Y=$0000 when hitting this code. And remember: the 65816 is little-endian (in case you wonder why some of the "values loaded" are "reversed"):

Code: Select all

        lda $00,x               ; Effective address $8F00, A=$0001 (bytes 0,1 of MetaspriteTable)
        beq metasprite_done     ; Not zero, don't branch
        inx                     ; X=$8F01
        lda $00,x               ; Effective address $8F01, A=$0000 (bytes 1,2 of MetaspriteTable0
        sta SpriteBuf1,y        ;
        inx                     ; X=$8F02
        lda $00,x               ; Effective address $8F02, A=$0100 (bytes 2,3 of MetaspriteTable)
        sta SpriteBuf1+1,y      ;
        iny                     ; Y=$0001
        inx                     ; X=$8F03
        iny                     ; Y=$0002
        iny                     ; Y=$0003
        iny                     ; Y=$0004

        lda $00,x               ; Effective address $8F03, A=$1001 (bytes 3,4 of MetaspriteTable)
        beq metasprite_done     ; Not zero, don't branch
        inx                     ; X=$8F04
        lda $00,x               ; Effective address $8F04, A=$0010 (bytes 4,5 of MetaspriteTable)
        sta SpriteBuf1,y        ;
        inx                     ; X=$8F05
        lda $00,x               ; Effective address $8F05, A=$0000 (bytes 5,6 of MetaspriteTable)
        sta SpriteBuf1+1,y      ;
        iny                     ; Y=$0005
        inx                     ; X=$8F06
        iny                     ; Y=$0006
        iny                     ; Y=$0007
        iny                     ; Y=$0008

        lda $00,x               ; Effective address $8F06, A=$??00 (bytes 6,? of MetaspriteTable)
        beq metasprite_done     ; ??? does this branch?  Depends on if the byte "past the end of the table" is zero!
Make sure you note the last couple lines. Compare that to what's in your table. I simply do not know what WLA DX is storing "after" the last byte you define in MetaspriteTable. But you want my guess? I'd be willing to bet you that it's got a non-zero value there -- for example it could be the first byte of .\\GamePictures\\hovertransport.map. Again (I'm like a broken record): a listing file would tell you this. And thus if your accumulator is 16-bit when that code runs, things would never end, and you'd end up with a loop that essentially writes all over RAM indefinitely (SpriteBuf1 points to $0400, but in an infinite loop with indexes being used, you would end up writing to all of memory space within $0000-FFFF ($0000-1FFF is RAM, the other writes would essentially do nothing right now)), and reads from all over the bank the code is executing it -- the result would be a total utter mess and almost certainly cause bizarre behaviour program-wise and visually.

P.S. -- This does not take someone "half a minute" to fix. My posts to you have actually taken hours of time as an aggregate (this one took almost 2 hours). Debugging someone else's code is always time-consuming because the person has to reverse-engineer it every single time (esp. when there's no clear explanation of what changed, i.e. "this is what it was before, and this is what I changed it to"). The author of the code should know what their program is doing better than someone who's being asked for help. Just a general rule, haha. :-)

And no, the developers manual would not help you here. This is purely a programming thing. I think you need to get more familiar with using a real-time debugger, because it could save you a lot of time assuming you know what it is you're looking at. Yes, the tools (emulator debuggers) tend to suck and they usually lack source code integration (so what you see in the debugger doesn't correlate with the code you wrote; all values are pre-assembled, again why a listing file is helpful...), so I understand the pain. But once you go through it a few times you'll get in the habit of knowing what you're seeing.

The one thing I wish emulators had was tie-ins for forcing a break/drop-to-debugger using native code. On the 65816, honestly I feel the best way to do this would be to use either BRK (but some games might use this legitimately?), COP, or WDM opcodes (the latter is certainly never used -- opcode $42 is the one leftover byte in the opcode table that is supposed to act as a NOP when run, so it'd work great for this, IMO). BRK and COP at least have 1 byte operands ("signature bytes"). It'd be really convenient to tell someone "Just put brk $ff where you want the debugger to kick in so you can step through your code", rather than futz with all of this.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: 16bit table indexing problem

Post by Drew Sebastino »

Wow, I'm a dumbass, I accidentally uploaded the wrong file. (That's why there is no phy, and phx because it is in the wrong folder.)
REAL Metasprite Demo.rar
(238.46 KiB) Downloaded 285 times
(no one needs to look at this, I'm just saying I had it.)

Edit: Wait! I'm even more of an idiot than I thought... I accidentally made it to where metaspritetest2 was going to metasprite1 instead of metasprite2 and I have metasprite2 at rep $20 (8bit?) instead of rep $10. Of course now, the code doesn't even take a minute to crash... (I think the problem was that I pushed to infinity because I didn't jump to the code that would pull.)
MORE REAL Metasprite Demo.rar
(238.63 KiB) Downloaded 287 times
Edit: (Again.) I solved the problem. I wrote sep #$10, rep #$20 instead of rep $10, sep #$20. After changed that, I ran it, used the restroom, then grabbed a soda and it was still running so I think It's safe to say it's alright. :wink: (By the way, I guess you should never rep right after you sep?)

Also, I guess the fact that I'm only writing inx once is one of the problems? (I thought it would automatically increment by 2 bytes instead of one.) The one thing I really do have to look at (which should be in the book) is the processor status bit things, because I don't have the slightest Idea of what I'm doing.

The reason why I always complain and come here for help is because I don't have the slightest clue as to what any of this means...
SNES9X Debugger.png
Maybe I should have tried something easier than the SNES for programming first... (I have no background knowledge.)
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: 16bit table indexing problem

Post by Drew Sebastino »

Actually, just forget everything I just said. I accidentally replaced the file that was correct with a one that was broken, and I tried to fix it, but I couldn't. I found out that the file that was working was different than the second one I posted. I'm hopelessly confused.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: 16bit table indexing problem

Post by koitsu »

Espozo wrote:Edit: (Again.) I solved the problem. I wrote sep #$10, rep #$20 instead of rep $10, sep #$20. After changed that, I ran it, used the restroom, then grabbed a soda and it was still running so I think It's safe to say it's alright. :wink: (By the way, I guess you should never rep right after you sep?)
I covered REP/SEP earlier, including what each bit represents. So let's cover it again, more verbosely. Remember: SEP/REP only touch (i.e. change) the bits you set to 1 in the operand -- all other bits are left alone. (The operand is essentially a mask of sorts). Below is a "chart" for you to use -- I know how SEP/REP work, but honestly I just remember this chart by heart because these are the most common operations you'll see:

Code: Select all

  rep #$10   ; X/Y = 16-bit (set bit 4 of P to 0).  $10 hex = %00010000 binary
  sep #$10   ; X/Y = 8-bit  (set bit 4 of P to 1).  $10 hex = %00010000 binary

  rep #$20   ; M = 16-bit (set bit 5 of P to 0).  $20 hex = %00100000 binary
  sep #$20   ; M = 8-bit  (set bit 5 of P to 1).  $20 hex = %00100000 binary

  rep #$30   ; M = 16-bit, X/Y = 16-bit (set bit 4 and 5 of P to 0).  $30 hex = %00110000 binary
  sep #$30   ; M = 8-bit,  X/Y = 8-bit  (set bit 4 and 5 of P to 1).  $30 hex = %00110000 binary
Using this "chart", you should be able to determine what you did, and why if you want something like 8-bit accumulator + 16-bit indexes that you need to use a combo of SEP and REP -- it cannot be done in a single instruction.
Espozo wrote:Also, I guess the fact that I'm only writing inx once is one of the problems? (I thought it would automatically increment by 2 bytes instead of one.)
Okay, two things here:

1. The increment opcodes (INC, INY, INX) only increment the register by 1. The "size" of the register has no bearing on "how much" is incremented. Rephrased: INC is equivalent to A++ or A = A +1, INY is equivalent to Y++ or Y = Y+1, INX is equivalent to X++ or X = X+1 (assuming you have some programming knowledge of other languages). The only bearing the register size (8 vs. 16-bit) has on this is when it comes time to increment from $FF to a new value (8-bit would go from $FF to $00, 16-bit would go from $FF to $100) or when incrementing a memory address (more on that at the end).

If you want to increment something by more than 1, you have a couple choices. For the X/Y registers, repeated calls to INX/INY would work (e.g. incrementing X by 2 would be INX / INX), but you should know that from your repeated INY statements within build_metasprite. :/

For incrementing the accumulator, you can use CLC / ADC #value (e.g. incrementing accumulator by 7 would be CLC / ADC #7).

There is no CLC/ADC equivalent that affects X or Y directly. If you wanted to increment X or Y by a larger value and don't want to have to call INX/INY repeatedly, then you have to transfer X or Y into the accumulator, use CLC/ADC, then transfer the accumulator back into X or Y. Example:

Code: Select all

  ldx #4    ; X=$0004
  txa       ; Transfer X into accumulator (A=$0004)
  clc       ; Clear carry
  adc #7    ; Add 7 to accumulator (A=$000B (11 decimal))
  tax       ; Transfer accumulator into X (X=$000B)
You will probably ask: "wouldn't doing TXA mean whatever (previously) was is the accumulator is lost?" -- yup, it does. That's why you'll find people doing PHA/PLA before/after that type of operation quite often, or using a temporary variable.

There is a point where repeated INY/INXs become space-wasting and time-wasting though (i.e. it's better to do something like TXA/CLC/ADC/TAX), but right now I really don't want to cover that because it'll just confuse you more. For now, if you wanted to increment X by, say, 12, then go ahead and do 12 INX instructions. If anyone comes along and says "this is inefficient blah blah blah", tell them you're still learning and to deal with one thing at a time. :-)

Also with INC (but not INX/INY), you can also increment (by 1) a value in memory without having to load it into the accumulator / write it back out. The active size (8 vs. 16-bit) of the accumulator matters here too (and can often cause people confusion, similar to what's happening presently). But again I don't want to cover that because it'll just add more confusion right now. Just know that it's available.

2. Terminology nitpick: you are not "incrementing by 2 bytes". You are incrementing an index register which you are using as an offset during your LDA.
Espozo wrote:The one thing I really do have to look at (which should be in the book) is the processor status bit things, because I don't have the slightest Idea of what I'm doing.
Well I've explained them twice now, including giving you a chart of essentially the most-commonly-used opcode+operand combinations are (I hope that helps you the most -- as I said, I understand how the opcodes work, but I honestly just remember the chart by heart. :-) ), so if you're still having problems then maybe referring to the WDC book is a better choice. There's a chapter on it, I believe.

One thing I should note: the learning curve you're going through here, with regards to 8-bit vs. 16-bit, is incredibly common, so don't feel alone! A lot of people used to the original 6502 and 65c02 (both 8-bit CPUs), when the 65816 came out, had very similar complexities/difficulties as what you're going through now. Doing 16-bit "stuff" on the 6502/65c02, however, is a lot more painful (IMO). The 65816 really spoils you -- it's one reason I have a harder time doing 6502, because I did a *lot* of my coding on the 65816 so using a native 8-bit CPU often makes me grumble. :-)

As for your SNES9x debugger shot: by default the SNES9x debugger doesn't execute any code, and instead stops at the very first instruction the processor wants to run. Step back for a moment and think about how the CPU "starts up". It starts executing code where the RESET vector points to, right? You should be able to determine where that is easily, and the debugger makes it even easier (there's a "Vector Info" button). The first instruction your program executes when the system starts is SEI. You should be able to find that in your code and go from there.

What you need to do is what I described earlier -- create a breakpoint on the execution of code, essentially right before you do your jsr build_metasprite. But as I said earlier, the debuggers do not currently have a way to "connect" source code to actual running code, so what you're seeing is quite literally what the assembled results are + what the CPU is doing. This is why I said it'd be very useful if emulators actually had a way to drop to the debugger when encountering an instruction (e.g. brk $ff), because then you could just add that line and run the code and then bam, continue from there.

A listing generation from your assembler would tell you exactly what memory address the jsr build_metasprite instruction is at, but WLA DX (as I've said a billion times now) is completely broken in this regard, which makes your job a lot harder. Essentially what you need to do right now is step through (run) every single line of code until you see code that looks very similar to the code around/right before jsr build_metasprite. If you aren't sure, do something that will clue you in: put a bunch of nop statements right before your jsr. Once you see those you'll know where they are, then you'll get an idea of what memory location the JSR is at, then can throw that into the debugger as an execution breakpoint, reset the system (there's a Reset button), click Run, and wait until it drops to the debugger and then you can step through your code instruction by instruction and see what it's doing, how the registers are affected, and so on. :-)

Learning how to use a debugger is a tedious process (about as tedious as programming) because each debugger behaves differently. There is no standard. I'm certain the bsnes debugger is substantially different from the SNES9x debugger, for example. The only one I have familiarity with is the SNES9x debugger, although as discussed in other threads NO$SNS has a debugger as well (although by default that emulator shows opcodes in a non-65xxx-syntax format -- you have to turn that feature off before you get something that's actually sane ;-) ).
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: 16bit table indexing problem

Post by koitsu »

nicklausw wrote:Visual Studio and a bunch of other crap is needed. Don't use the binaries given on Ville's website, as those are somewhat outdated (and some bug fixes along with improvements have been made since then).

By the assumption that you're using Windows, here's my binaries. Fresh off GitHub. Made them last month as I recall.

EDIT: just remembered I also compiled them for Ubuntu, so I'll put those there aswell. Zip = windows, Tar = Ubuntu.
The Windows binaries you provide in the zip are native 64-bit binaries -- they will not work on 32-bit operating systems. This is unlike the "unofficial" binary builds which are 32-bit. I can't use these binaries (I do not run a 64-bit OS). There's really no reason (that I can think of) for native 64-bit binaries for WLA DX on Windows (on *IX it's a different situation, as not everyone's OSes have 32-bit compatibility shims; for example my FreeBSD boxes are all pure 64-bit with absolutely no 32-bit binary support). Windows 64-bit OSes provide 32-bit compatibility shims by default, so 32-bit is a better choice there for something non-memory-intensive like 65xxx assemblers.

The only reason I'm bothering to try your binaries is to see if the listing generation stuff has been fixed. "And some bug fixes along with improvements have been made since then" isn't precise enough -- this is exactly what a ChangeLog or commit history is for. I wouldn't need to try the binaries if I could see that + know exactly what commit and/or branch your binaries were based off of. :-)
User avatar
nicklausw
Posts: 376
Joined: Sat Jan 03, 2015 5:58 pm
Location: ...
Contact:

Re: 16bit table indexing problem

Post by nicklausw »

koitsu wrote:
nicklausw wrote:Visual Studio and a bunch of other crap is needed. Don't use the binaries given on Ville's website, as those are somewhat outdated (and some bug fixes along with improvements have been made since then).

By the assumption that you're using Windows, here's my binaries. Fresh off GitHub. Made them last month as I recall.

EDIT: just remembered I also compiled them for Ubuntu, so I'll put those there aswell. Zip = windows, Tar = Ubuntu.
The Windows binaries you provide in the zip are native 64-bit binaries -- they will not work on 32-bit operating systems. This is unlike the "unofficial" binary builds which are 32-bit. I can't use these binaries (I do not run a 64-bit OS). There's really no reason (that I can think of) for native 64-bit binaries for WLA DX on Windows (on *IX it's a different situation, as not everyone's OSes have 32-bit compatibility shims; for example my FreeBSD boxes are all pure 64-bit with absolutely no 32-bit binary support). Windows 64-bit OSes provide 32-bit compatibility shims by default, so 32-bit is a better choice there for something non-memory-intensive like 65xxx assemblers.

The only reason I'm bothering to try your binaries is to see if the listing generation stuff has been fixed. "And some bug fixes along with improvements have been made since then" isn't precise enough -- this is exactly what a ChangeLog or commit history is for. I wouldn't need to try the binaries if I could see that + know exactly what commit and/or branch your binaries were based off of. :-)
I just did the classic ol' https://github.com/vhelin/wla-dx/tarball/master, last day of last year. Also was not aware the binaries were 64 bit...will see if I can compile 32-bit.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: 16bit table indexing problem

Post by Drew Sebastino »

Right now, the main problem isn't trying to convert the code to 16bit, because I need to first carry the information so when I do convert the code (If I can) part of the memory won't be erased. I'm about to show you this (don't worry! This doesn't take two hours.) because it makes 0 sense to me. I have the normal code:
Metasprite1.png
and I have the code that is exactly the same, except I added phy, phx, plx, and ply:
Metasprite2.png
The results...:
Results....png
This has made me extremely frustrated. I 1. made sure I pushed and pulled the same amount of times and 2. pulled in the reverse order I pushed, but I doesn't work.

Sorry for relying on you so heavily, koitsu, it's just that, like I said, I looked over it and still don't see any problems. One thing I wonder is if those php and plp things have to do with it... (They both start with P like phx or ply? :P )
User avatar
Movax12
Posts: 541
Joined: Sun Jan 02, 2011 11:50 am

Re: 16bit table indexing problem

Post by Movax12 »

Pushing, pulling use the stack and so does JSR. (Unless there is something I don't know abut 65816, you are also PHP, then PLX.)
Last edited by Movax12 on Mon Jan 19, 2015 5:22 pm, edited 1 time in total.
Post Reply