Jump to content
IGNORED

How to Organize sdata?


Cybearg

Recommended Posts

From what I understand, the reason sdata is able to hold so much more than a normal data set is because it is essentially a 2d array, while a data set is more like a 1d array. Therefore, an sdata set can hold 256^2, or 65536 bytes of data, far more than a single bank making it as large as one could use it.

 

According to the link posted, two sequential bytes are set aside, but do those two bytes work together to create a 16-bit register to address the sdata's data, like a superlong 1d array, or do they work separately to create a 2d array, as mentioned?

 

I ask in the interest of creating a scrollable playfield. Evidently, the only way to read data from an sdata array is using var = sread(_SData_Set), so it will read from whatever the bytes aside from the sdata depict, but how would one then move along the sdata set, say to read playfield data?

 

For instance, if I have 4 x 11 = 44 bytes per full room and wanted nine rooms in a square of 3x3, which would take 396 bytes of ROM, how would I need to address that data in order to account for both horizontal and vertical shifts?

 

My guess would be that an sdata set is a very long 1d array using the two bytes as a 16-bit register to reference them. If I had the aforementioned data set as:

 

 

   sdata _Playfield = a
   ...
end

 

... and the player started in the center-most of the 9 screens, I would do something like:

 

 

    a = 4 * 44
    ...
    for temp1 = 0 to 47
        temp2 = a + temp1
        var0[temp2] = sread(_Playfield)
    next

 

And, if so, then I suppose that shifting would all come down to how the playfield data was arranged, correct? For instance, if the 12 bytes of the first row of the top-most 3 rooms would come first, followed by the 12 bytes of the second row of the top-most three rooms, etc. moving down, how would one accomplish shifts left/right and up/down?

 

In this particular data configuration, up/down shifts should be as easy as adding or subtracting 12 from a for each vertical shift, but how would one account for horizontal shifts? This would be much simpler if a and b were treated as a 2d array, with one specifically being rows and the other columns, rather than an upper and lower byte of a 16-bit register.

 

So, in conclusion (after that speculative ramble), how do the two bytes of an sdata set work, how should playfield data be organized in an sdata set, and how can directional shifts for playfield data in an sdata set be accomplished?

Edited by Cybearg
Link to comment
Share on other sites

I think the best reference is still R.T.s

http://www.randomterrain.com/atari-2600-memories-batari-basic-commands.html#dataarrays

 

I rather like normal DATA statements. Doesn't use up 2 variables and you can use them like a normal array.

But a normal array can only hold up to 256 bytes of data. If (like I plan) I'm using the superchip to get a 4x22 grid, that's 88 bytes per screen, limiting me to 2 full screens that can fit in an array. It's not enough.

Link to comment
Share on other sites

I don't think bB cares how big you make
a data statement it's just that you wont
be able to address more than the first
256 bytes with the normal syntax without
going through some gyrations

If you want random access to more than
256 bytes you're going to have to go
through those gyrations in bB by
selecting amongst data references

I think sdata has no particular
advantage unless you're going to
address the data serially.

A little asm would help, it wouldn't
be too hard to write peek and poke
macros (which would suck. One of the
things I really like about bB is that
you can reference memory as an array)

Link to comment
Share on other sites

But a normal array can only hold up to 256 bytes of data.

 

That's because the data is read from the array using an index, and an index is a 1-byte register that can have a value of only 0 through 255. So the data you're reading must be at Array+X, where Array is the starting address of the data and X is the 1-byte index (the 6507's X register, not bB's X variable), therefore the range is Array+0 through Array+255. The disadvantage of using indexed data is that (normally) the data can't exceed 256 bytes, but the advantage is that it can be read randomly. To get around the 256-byte limitation you can break the data into 256-byte pages, each page having its own address, and read from the desired page-- e.g., ArrayPage3[138].

 

With sdata there's no index per se, because the entire address is stored as a 2-byte pointer. Note that bB's instructions about using sdata stress that your program must run over the sdata itself before you try to use it (I didn't look up the exact wording, but it's something like that). That's because when bB encounters the sdata it stores the starting address in the 2-byte pointer. Then as you read from the sdata the address of the pointer is updated so that it always points to the next unread byte of data. You aren't reading from an indexed position per se, but from a 2-byte address. The range is 256*Hi+Lo, where Hi is the hi-byte of the address and Lo is the lo-byte. Since Hi and Lo both have ranges of 0 through 255, the range for the sdata is 256*0+0 through 256*255+255, or 0 through 65535. The advantage of sdata is that the data can exceed 256 bytes, but the disadvantage is that (normally) it can't be read randomly. You can get around the non-random limitation by setting the 2-byte pointer yourself-- e.g., if you want to randomly access the data at offset 1624, you'd need to set the pointer to the address Array+1624.

Link to comment
Share on other sites

For instance, if I have 4 x 11 = 44 bytes per full room and wanted nine rooms in a square of 3x3, which would take 396 bytes of ROM, how would I need to address that data in order to account for both horizontal and vertical shifts?

 

 

Let's assume the sdata looks something like this:

 

 

   sdata roomdata=a
      ; room 1's data
      ; room 2's data
      ; room 3's data
      ; room 4's data
      ; room 5's data
      ; room 6's data
      ; room 7's data
      ; room 8's data
      ; room 9's data
end

 

To start reading from a given location within the sdata, either you're going to have to calculate the desired address yourself-- which would be a bit of a hassle-- or you can define some constants and let the assembler do all of the hard work for you:

 

 

   const room1lo = <roomdata
   const room1hi = >roomdata
   const room2lo = <(roomdata+44)
   const room2hi = >(roomdata+44)
   const room3lo = <(roomdata+88)
   const room3hi = >(roomdata+88)
   ; ...
   const room9lo = <(roomdata+352)
   const room9hi = >(roomdata+352)

 

Then when you want to read the data for a particular room you just set the sdata pointer and start reading:

 

 

   ; to read the data for room 6
   a = room6lo : b = room6hi ; we defined the sdata to be variable a, so the lo-byte is in a and the hi-byte is in b
   ; now read the 44 bytes of data sequentially:
   var0 = sread(roomdata)
   var1 = sread(roomdata)
   var2 = sread(roomdata)
   ; etc.
   var47 = sread(roomdata)

 

You could also use a for loop or nested for loop.

Link to comment
Share on other sites

That's because the data is read from the array using an index, and an index is a 1-byte register that can have a value of only 0 through 255. So the data you're reading must be at Array+X, where Array is the starting address of the data and X is the 1-byte index (the 6507's X register, not bB's X variable), therefore the range is Array+0 through Array+255. The disadvantage of using indexed data is that (normally) the data can't exceed 256 bytes, but the advantage is that it can be read randomly. To get around the 256-byte limitation you can break the data into 256-byte pages, each page having its own address, and read from the desired page-- e.g., ArrayPage3[138].

That would be fine, except that it would require code to jump between the pages, which could be clumsy if a room was half of one page and half of another page, wouldn't it? It'd need some sort of on..goto statement to switch between data sets, as I don't know of any way to do so elegantly without on..goto

 

With sdata there's no index per se, because the entire address is stored as a 2-byte pointer. Note that bB's instructions about using sdata stress that your program must run over the sdata itself before you try to use it (I didn't look up the exact wording, but it's something like that). That's because when bB encounters the sdata it stores the starting address in the 2-byte pointer. Then as you read from the sdata the address of the pointer is updated so that it always points to the next unread byte of data. You aren't reading from an indexed position per se, but from a 2-byte address. The range is 256*Hi+Lo, where Hi is the hi-byte of the address and Lo is the lo-byte. Since Hi and Lo both have ranges of 0 through 255, the range for the sdata is 256*0+0 through 256*255+255, or 0 through 65535. The advantage of sdata is that the data can exceed 256 bytes, but the disadvantage is that (normally) it can't be read randomly. You can get around the non-random limitation by setting the 2-byte pointer yourself-- e.g., if you want to randomly access the data at offset 1624, you'd need to set the pointer to the address Array+1624.

But to read the address Array+1624, you'd actually be setting Hi = (1624 / 255) : Lo = (1624 & 255), right?

 

I suppose that, in the end then, sdata may be the most difficult way of indexing the data because of the need to shift where the data is being read from in order to account for rows and columns.

 

For instance, if I had a set of data for 10 rooms (480 bytes), arranged like so:

 

column0-row0, column1-row0, column2-row0... column39-row0, column0-row1, column1-row1, ... column39-row11

 

... then code that reads from the array would be like:

 

    for i = 0 to 11
       k = 0
loopjump
        if k > 3 then goto skiptonext
           temp1 = (i * 4) + k
           temp2 = indexposition + (i * ARRAYWIDTH) + k
           var0[temp1] = _DataArray[temp2]
        k = k + 1: goto loopjump
skiptonext
    next
... Right? I mean, forgetting for a moment that the above code wouldn't work on a 2600, that's essentially what I'm doing, yes? And to facilitate a shift up, I would subtract ARRAYWIDTH from indexposition and add ARRAYWIDTH to indexposition to shift down, while horizontal shifts would just be a matter of adding or subtracting 1 from indexposition.

 

So then what would actually be the best way to do this for large, 4-directional scrolling playfield data?

 

Then when you want to read the data for a particular room you just set the sdata pointer and start reading:

 

 

   ; to read the data for room 6
   a = room6lo : b = room6hi ; we defined the sdata to be variable a, so the lo-byte is in a and the hi-byte is in b
   ; now read the 44 bytes of data sequentially:
   var0 = sread(roomdata)
   var1 = sread(roomdata)
   var2 = sread(roomdata)
   ; etc.
   var47 = sread(roomdata)

You could also use a for loop or nested for loop.

 

That sounds great if I wanted a bunch of individual rooms stored together in a single large sdata set, but how would I index half one room and half of another room? 1/4 of one room, 1/4 of another room, 1/4 of yet another room, and 1/4 of a fourth room, in the case that the player was standing right on the intersection of the corners of four rooms? How would I handle horizontal/vertical playfield scrolling with this? Edited by Cybearg
Link to comment
Share on other sites

If you want the flexibility of data with the larger sizes of sdata, the usual solution is to break up the data into smaller chunks and keep them in different data statements.

 

e.g. if you want 9 3x3 rooms, just store the strips of data in different data structures.

 

  data roomrow1
  [data here]
end
  data roomrow2
  [data here]
end
  data roomrow3
  [data here]
end

The format is split up and less easy for you to read and edit, but that's the cost of a less resource intensive solution.

 

The difference between data and sdata boils down to limitations in 6502/7 indexed mode and complexity of its indirect mode. If bB were to be enhanced to allow random access to sdata, you'd be blowing a bunch of rom and cycles on 16-bit math every single time you tried to access an element. No bueno.

Link to comment
Share on other sites

 


 


The disadvantage of using indexed data is that (normally) the data can't exceed 256 bytes, but the advantage is that it can be read randomly. To get around the 256-byte limitation you can break the data into 256-byte pages, each page having its own address, and read from the desired page-- e.g., ArrayPage3[138].

That would be fine, except that it would require code to jump between the pages, which could be clumsy if a room was half of one page and half of another page, wouldn't it? It'd need some sort of on..goto statement to switch between data sets, as I don't know of any way to do so elegantly without on..goto

 

In this particular case, I'm inclined to think that sdata is the better solution.

 

 


The advantage of sdata is that the data can exceed 256 bytes, but the disadvantage is that (normally) it can't be read randomly. You can get around the non-random limitation by setting the 2-byte pointer yourself-- e.g., if you want to randomly access the data at offset 1624, you'd need to set the pointer to the address Array+1624.

But to read the address Array+1624, you'd actually be setting Hi = (1624 / 255) : Lo = (1624 & 255), right?

 

No, you'd need to set Hi = (Array + 1624) / 255 and Lo = Array + 1624 - 256 * Hi. You can't do that in bB, because bB doesn't have 16-bit math. That's why it's easier, given what you're wanting to do, to define constants for the hi and lo bytes of the various places you want to start reading from in the sdata (since the assembler will do all the math for you when you're compiling your program).

 

Before I try to answer any of your other questions, I need to understand what you mean by a scrolling playfield. When you said 9 rooms of data arranged in a 3x3 grid, I thought you were going to be moving from room to room. If that's not the case-- if you're actually talking about scrolling around within a large playfield-- then I'll have to rethink this, and I'm kind of fuzzy-headed right now because of flu medicine. However, there are a couple of topics that talk about scrolling large playfields.

Link to comment
Share on other sites

Before I try to answer any of your other questions, I need to understand what you mean by a scrolling playfield. When you said 9 rooms of data arranged in a 3x3 grid, I thought you were going to be moving from room to room. If that's not the case-- if you're actually talking about scrolling around within a large playfield-- then I'll have to rethink this, and I'm kind of fuzzy-headed right now because of flu medicine. However, there are a couple of topics that talk about scrolling large playfields.

Yeah, that's what I mean--a data statement holding a mega-room that's the size of 9 rooms arranged in a 3x3 grid, which can scroll in and out. I'm investigating the possibility of a Zelda 2-style game, with an overworld map and sidescrolling rooms/dungeons, or a more Metroid-like game with a single large map.

 

The format is split up and less easy for you to read and edit, but that's the cost of a less resource intensive solution.

That doesn't matter to me. I'll be making a map editor in Gamemaker or something to ease the editing/output of the proper format. I just need to know how the data will be structured first and have some idea of how it will work.

 

Also, if I had maps across multiple data sets, would I be able to feasibly read partially from one set, then another, then another, in order to do scrolling? How would that sort of logic look?

Edited by Cybearg
Link to comment
Share on other sites

Yeah, that's what I mean--a data statement holding a mega-room that's the size of 9 rooms arranged in a 3x3 grid, which can scroll in and out. I'm investigating the possibility of a Zelda 2-style game, with an overworld map and sidescrolling rooms/dungeons, or a more Metroid-like game with a single large map.

 

 

In that case, I'd suggest looking at the threads that talk about scrolling a large playfield.

 

Note that data and sdata in batari Basic are just two different approaches to the same thing-- letting you read data from an array. They can accomplish the same things, but the methods will necessarily be different, so you end up picking the one that's faster and requires less work, or maybe the one you understand the best.

 

For a large 3x3 scrolling playfield, the data should be stored differently than for a grid of 3x3 screens:

 

3x3 grid:

byte 1 = screen 1, row 1, column 1

byte 2 = screen 1, row 1, column 2

byte 3 = screen 1, row 1, column 3

byte 4 = screen 1, row 1, column 4

byte 5 = screen 1, row 2, column 1

byte 6 = screen 1, row 2, column 2

etc.

 

In other words, for a grid you'd store each screen as a separate screen, with its own rows and columns (byte columns, not pfpixel columns). But for a large playfield you'd want to treat the entire set of data as a single playfield. You could organize it either by row or by column:

 

3x3 playfield, organized by rows:

byte 1 = row 1, column 1

byte 2 = row 1, column 2

byte 3 = row 1, column 3

byte 4 = row 1, column 4

byte 5 = row 1, column 5

byte 6 = row 1, column 6

etc.

byte 12 = row 1, column 12

byte 13 = row 2, column 1

byte 14 = row 2, column 2

etc.

 

3x3 playfield, organized by columns:

byte 1 = column 1, row 1

byte 2 = column 1, row 2

byte 3 = column 1, row 3

byte 4 = column 1, row 4

etc.

 

Depending on whether you want to use (indexed) data or sdata, the method you use to organize the data may impact how large the playfield can be, since an indexed array is limited to 256 bytes. So if you organize by columns, each column could be up to 256 bytes tall, which is just over 23 screens tall. But if you organize by rows, each row could be up to 256 bytes long, which is 64 screens wide. So for your situation either method would be fine, but I'd probably go with organizing by columns.

 

Anyway, you could use either an indexed array, or sdata, depending on which is easier for you to understand or (if both are equally easy/difficult for you) which is faster or takes less ROM for the routines (depending on whether speed or space is the more important consideration).

 

Using indexed arrays:

 

 

   data column1
   ; 33 bytes of data here
end
   data column2
   ; data
end
   data column3
   ; data
end
   ; etc.

 

For sdata it's the same, except you'd do it as a single set of data:

 

 

   sdata roomdata=a
   ; 33 bytes of data for column 1
   ; 33 bytes of data for column 2
   ; 33 bytes of data for column 3
   ; 33 bytes of data for column 4
   ; 33 bytes of data for column 5
   ; etc.
end

 

With the indexed array you can just use the names of the tables (column1, column2, column3, etc.) to access the data, but with sdata you'd want to define some constants for the lo-/hi-byte pointers as mentioned previously:

 

 

   const column1lo = <roomdata
   const column1hi = >roomdata
   const column2lo = <(roomdata+33)
   const column2hi = >(roomdata+33)
   const column3lo = <(roomdata+66)
   const column3hi = >(roomdata+66)
   ; etc.
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...