It is currently Tue Nov 13, 2018 11:09 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Sun Apr 05, 2015 11:56 am 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 321
So, I have plenty of places right now that are being indirectly addressed, but now that I'm sort of creating a jump table (indirect address pointing to indirect addresses), can't quite wrap my head around it. Hoping someone can help.

So, essentially I want something like this:

Code:

StartAddressLo:
    .db <newAddLo0, <newAddLo1, <newAddLo2
StartAddressHi:
    .db >newAddHi0, >newAddHi1, >newAddHi2



And then use that info to point to another table, which provides a data address...

Code:

NewAddLo0:
    .db <PointHereLo0, <PointHereLo1, <PointHereLo2
NewAddHi0:
    .db >PointHereHi0, >PointHereHi1, >PointHereHi2




So if I had three variables, StartVariable, temp, and PointHere variable...trying something like this:


Code:

    LDX StartVariable     ;;;(let's pretend it's #$00 right now)
    LDA StartAddressLo,x
    ;; So now, that should read newAddLo0 ?
    STA temp  ;; which is now loaded into the point here variable
    LDX PointHere ;; let's pretend #$01 was loaded into that variable
    LDA temp,x
    ;; the loaded address should now be PointHereLo1?




I'm sure I'm probably circling around something here, but not quite there. Could someone point me in the right direction as to how to achieve this sort of address jump table?

Thanks!


Top
 Profile  
 
PostPosted: Sun Apr 05, 2015 1:05 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6948
Location: Canada
1. Did you forget about StartAddressHi? Pointers are 16-bit, i.e. two bytes. You'll need to fill two bytes: "temp+0" and "temp+1"

2. "LDA temp, X" is not indexed addressing, this is like "LDA temp+X", what you want it "LDA (temp), Y". The () is part of the notation for indirection, and the Y register has to be used as well, not X.

The example is a bit strange since you're going through two indirections (maybe better to learn one indirection first?), but going along with it:

Code:
ldx StartVariable ; assuming StartVariable is currently storing 0 for example
lda StartAddressLo, X
sta temp+0
lda StartAddressHi, X
sta temp+1 ; (temp) is now a pointer to newAddLo0
ldy PointHere ; assuming PointHere is currently storing 1 for example
lda (temp), Y
sta ptr+0
iny ; EDIT this is wrong, see below
lda (temp), Y ; EDIT this is also wrong, see below
sta ptr+1 ; (ptr) is now a pointer to PointHereLo1
ldy #0
lda (ptr), Y ; this loads the value pointed to by PointHereLo1


Also, pointer variables have to be on the zero page, so both "temp" and "ptr" in this example need to be somewhere on the ZP. (The exception is an indirect JMP, which can use a pointer stored anywhere.)

EDIT: My example wasn't correct either, I stumbled on the second misdirection, new example below...


Last edited by rainwarrior on Sun Apr 05, 2015 1:39 pm, edited 2 times in total.

Top
 Profile  
 
PostPosted: Sun Apr 05, 2015 1:34 pm 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 321
Yes, I also had the high bytes - was just reducing for the post. The parentheses might be the issue though! Hopefully! Let me check it out.


Top
 Profile  
 
PostPosted: Sun Apr 05, 2015 1:50 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6948
Location: Canada
Sorry, I just realized there are more things wrong with the example. The second layer of indirection kinda threw me off when I was reading it.

First, the StartAddress striped array is incorrect. You are taking the low address (<) of a low byte table and a high address (>) if a high byte table. The way you're doing it you actually need TWO StarAddress tables (one to address NewAddLo# and a second one to address NewAddHi#).

Code:
StartAddressLoLo: .byte <NewAddLo0, <NewAddLo1, <NewAddLo2
StartAddressHiLo: .byte >NewAddLo0, >NewAddLo1, >NewAddLo2
StartAddressLoHi: .byte <NewAddHi0, <NewAddHi1, <NewAddHi2
StartAddressHiHi: .byte >NewAddHi0, >NewAddHi1, >NewAddHi2


I'm going to stop there, though, as you can see how weird this is getting. I would strongly suggest NOT striping the second array. Instead do it like this:

Code:
; striped StartAddress table
StartAddressLo: .byte <NewAdd0, <NewAdd1, <NewAdd2
StartAddressHi: .byte >NewAdd0, >NewAdd1, >NewAdd2

; unstriped NewAdd table
NewAdd0: .word PointHere0, PointHere1, PointHere2
; etc...

ldx StartVariable
lda StarAddressLo, X
sta temp+0
lda StartAddressHi, X
sta temp+1 ; (temp) is now a pointer to NewAdd# (# = StartVariable)
lda PointHere
asl ; multiply PointHere by 2 to address unstriped table
tay
lda (temp), Y
sta ptr+0
iny ; advance to second byte
lda (temp), Y
sta ptr+1 ; (ptr) is now a pointer to PointHere# (# = PointHere)
ldy #0
lda (ptr), Y ; this loads the value pointed to by PointHere0


The reason I am suggesting to not stripe the second array is because when striping each layer of indirection multiplies the number of arrays needed by 2. I could write out an example of how to do it but it gets annoyingly complicated. Much easier to just multiply your index by 2 and store it as words. You could even store the first array as words too, striping into bytes is more of an optimization than a necessity (unless you need more than 128 entries).

Anyhow... this is also an example of why I'm suggesting to figure out one indirection first. ;P


Last edited by rainwarrior on Sun Apr 05, 2015 2:26 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Sun Apr 05, 2015 2:14 pm 
Offline

Joined: Mon Apr 01, 2013 11:17 pm
Posts: 437
Do you really need so many levels of indirection?

To put it in terms of C, are you doing array[i][j] when you could be doing array[i * WIDTH + j]?


Top
 Profile  
 
PostPosted: Sun Apr 05, 2015 2:16 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3676
Location: Mountain View, CA
Comments and questions in passing:

1. Comment: I think the reason the indirection is so hard to understand in this case is because of the use of label names that look almost similar. No, I am not saying avoid labels. I'm saying for learning how indirect addressing works, referring to literal values that are unique (ex. $1234 vs. $ABCD) is extremely helpful and makes comprehension easier.

2. Question: is there some particular reason the low and high bytes of the effective address (what is to be used for the indirection) are in separate address ranges, rather than in an "endian format" (i.e. little endian (low,high), e.g. $FA26 would be .db $26,$FA)? That is one of the things which is also making this whole question/issue way more complicated than it needs to be.

If this is a romhacking effort and the existing data is already in that format, then I understand, but if you're adding your own tables (hence "indirect to indirect to raw data") and having to write the code, then you might as well use something sane. Plus it would allow you to say things like .dw $FA26 rather than .db $26,$FA

Footnote: my questions/comments should **not** de-rail the thread. If the OP has issues understanding indirect addressing, I am more than happy to explain that (rainwarrior has done a great job already, but as he states it's become convoluted because of the format of the data and how things are being done). Indirect addressing is amazingly simple, super useful, and really not hard to understand -- it's that initial hump that you gotta get past, but once you do so it sticks like glue.


Top
 Profile  
 
PostPosted: Sun Apr 05, 2015 2:37 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6948
Location: Canada
Joe wrote:
To put it in terms of C, are you doing array[i][j] when you could be doing array[i * WIDTH + j]?

On the 6502 the former is often more sensible. The 8-bit index register really limits your array size (is WIDTH * HEIGHT < 256?) and if WIDTH is not a power of 2, the multiplication is anything but trivial.

Actually, even in C, the former is often more sensible. If the array a simple 2D array (and not a more complicated array of pointers), when it's compiled it will be identical to [i*WIDTH+j]. So... unless you've got a specific reason why you need the latter (and such reasons do exist), it might be better to stick with [i][j], since it's less typing and less error prone.


Top
 Profile  
 
PostPosted: Sun Apr 05, 2015 3:08 pm 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 321
I think I may have misprinted some of that when I was originally copying it over (to try and get something conceptual). Maybe if I lay out exactly what I'm trying to do, you guys might provide me with a best method. Maybe I'm overcomplicating it anyway.

I'm trying to make my calls for level data more efficient....whereby I store x amount of 'rooms' of level data in each bank. Let's say there are 16 'rooms' per bank. In my static bank, I have a variable which can determine what room to pull, 0-15. But I also need a pointer to determine which room data to grab based on what bank we're in. So, if I might be in 'room 2 of bank 11' or something. The easiest method I could come with was have a table that held them all in the static bank, and did some math to the bank number to determine which 'table row'...so pointers to room data 0-15 would be in row 0, 16-31 in row 1...etc etc etc. However, I was trying to conceive of a way that might be even easier to read - where it was always looking to room 0-15, but the level data would be determined based on bank number.

How do you guys handle something like this?


Top
 Profile  
 
PostPosted: Sun Apr 05, 2015 4:13 pm 
Offline

Joined: Mon Apr 01, 2013 11:17 pm
Posts: 437
Assuming variable-length data, I would use one table with the bank numbers and one or two tables for addresses. This would let me pack the room data in a way that doesn't cross bank boundaries. Put the address in the zero page, switch to the appropriate bank, and use indirect addressing to read the room data. (None of these tables need to be in the fixed bank, by the way.)

If, for some reason, you have to keep the rooms in their respective banks, I'd just throw the address table in the switched bank with the room data and let bank switching be my first level of indirection. As long as all of the tables end up at the same address, it will work fine.

Assuming fixed-length data, I'd do some math on the room number to get the bank and address. No tables necessary (unless you use some to accelerate the calculation).


Do my assumptions hold correctly for your data? If not, I'll need more information.


Top
 Profile  
 
PostPosted: Sun Apr 05, 2015 5:39 pm 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 321
hey Joe - thanks for the advice. I'm still a novice at this, for sure, but I'm getting the hang of things.

I do have a table with bank numbers. That's already happening. My concept has been to this point just .include - ing level data under easily defined labels...for instance, in bank0 I might have:

Code:

bank0_rm0:
    .include "RoomData\bank0rm0.asm"
    .include "CollisionData\bank0rm0col.asm"


Or something comparable, and then have a big table in the static page that just had:

Code:

NametableData:
    .dw bank0_rm0, bank0_rm1, bank0_rm2.....bank0_rm10
    .db bank1_rm0, bank1_rm1, bank0_rm2....bank0_rm10



...and then, when loading tile data/nametable data, loading in the correct bank and loading the proper 'room number' in the table.

It just seemed completely gratuitous since each bank would (seem) to have about 16 rooms worth of data (or at the very least, could be made to be fixed length...if I had to reduce it to 9 or could squeeze in 20 after some compression, they'd be set length). It seemed that instead I could have JUST the rm0-rm10 being accounted for, and then in each bank, having data that corresponded. That's what I'm having a problem with setting up, I guess. I feel like I'm close to a solution but haven't quite arrived there yet.


Top
 Profile  
 
PostPosted: Mon Apr 06, 2015 1:08 am 
Offline

Joined: Mon Apr 01, 2013 11:17 pm
Posts: 437
JoeGtake2 wrote:
I do have a table with bank numbers. That's already happening.
But why do you split them up by bank and then by room number? Why not have only a room number, so you can easily move rooms between banks?

For example:
Code:
; in bank 0
Room0:
    .include "room0.asm"
Room2:
    .include "room2.asm"

; in bank 1
Room1:
    .include "room1.asm"

Then have some tables like this:
Code:
RoomBankTable:
    .db 0, 1, 0
RoomDataTable:
    .dw Room0, Room1, Room2

JoeGtake2 wrote:
It just seemed completely gratuitous since each bank would (seem) to have about 16 rooms worth of data (or at the very least, could be made to be fixed length...if I had to reduce it to 9 or could squeeze in 20 after some compression, they'd be set length).
By "fixed length", I mean "every room takes up exactly the same number of bytes". In that case, you can simplify things by multiplying the room number by the size of a room, and adding the start address of the switchable bank. (You might end up using a table of precalculated results for this.)
Code:
; bank 0
.include "bank0rm0.asm"
.include "bank0rm1.asm"
...
; repeat for bank 1, bank 2, etc.

JoeGtake2 wrote:
It seemed that instead I could have JUST the rm0-rm10 being accounted for, and then in each bank, having data that corresponded. That's what I'm having a problem with setting up, I guess. I feel like I'm close to a solution but haven't quite arrived there yet.
For variable-length rooms, you need a table located at the same address (at the very beginning) in every bank. Use the address of the switchable bank as the address of the room table. Something like this:
Code:
; bank 0:
    .dw bank0rm0, bank0rm1, ...
bank0rm0:
    .include "bank0rm0.asm"
bank0rm1:
    .include "bank0rm1.asm"

; bank 1
    .dw bank1rm0, bank1rm1, ...
bank1rm0:
    .include "bank1rm0.asm"

Still, I prefer the flexibility of the first example.


Top
 Profile  
 
PostPosted: Mon Apr 06, 2015 6:03 am 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 321
If I'm understanding what you're saying (with the first example), that's sort of how I have it now. If my game has 160 rooms, for instance (that's 10 banks of 16 rooms), it might look like:

Code:

;;Bank 0
rm0:
    .include "Room0.asm"
rm1:
    .include "Room1.asm"
....
rm0F:
    .include "Room0F.asm"



and then the next bank might look like...

Code:

;;Bank 1
rm10:
    .include "Room10.asm"
rm11:
    .include "Room11.asm"
...
rm1F:
    .include "Room1F.asm"


And then have a table that lays out...

Code:

AllPossibleRooms:
    .dw rm0, rm1, rm2...(all 160 rooms in this table)...rm9F




Is this your general suggestion? That's more or less how I have it now.


Top
 Profile  
 
PostPosted: Mon Apr 06, 2015 7:15 am 
Offline

Joined: Mon Apr 01, 2013 11:17 pm
Posts: 437
That's basically it, but with a second table of bank numbers so I can move the room data between banks. That way, if rooms 0 through F are too big to all fit in a single bank, I can move one of them elsewhere.

I would not use this method if the room data is always the same number of bytes per room.


Top
 Profile  
 
PostPosted: Mon Apr 06, 2015 8:10 am 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 321
Alright, maybe I'll just tweak the method I have rather than making it unnecessarily more difficult than it has to be. The room data will likely not be exactly the same, but the idea is to have it all fit inside of the bank container...so x rooms in bank 0 might take up 75% of the bank space where as rooms in bank 1 might take up 62%, but the room numbers per bank would be equal and the number of rooms per bank would be determined by taking a look at the fit of the largest grouping.

Thanks for the feedback on that.


Top
 Profile  
 
PostPosted: Mon Apr 06, 2015 8:27 am 
Offline

Joined: Mon Apr 01, 2013 11:17 pm
Posts: 437
You're welcome.

I still don't understand why you're limiting yourself to a specific number of rooms per bank. The first method I proposed earlier would allow you to pack any number of rooms per bank, and in any order, so you could minimize wasted space.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 4 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