Jump to content
IGNORED

Assembly and sprites question


jchase1970

Recommended Posts

This is a how best to do this question more then anything.

 

Ok, I'm using alot of patterns for double sized sprites. If I want to change the image of a sprite is it best the change the pattern address in the ATTRIBUTE TABLE or load in another image into the DESCRIPTOR TABLE.

 

A 16x16 sprite is 32bytes and sounds like alot to move but is there really much of a hit in speed to do this?

 

I'm alittle confused by what address is what,

Pattern Descriptor Table is at >800 and starts with pattern >00

Sprite Descriptor Table is at >400 and starts with pattern >80

 

I'm not sure how much space I have, do I have >800->400 space for sprite patterns which is >400,1024bytes,128 patterns at 4 patterns per sprite =32 sets. I may have 32 sprites but I wont be using 32 different pattern sets so 8-10 will be the same pattern and the Attribute Table for these can point to the same address in the Descriptor Table, RIGHT?

 

If the Sprite Attribute Table is at >300 and the Sprite Descriptor Table is at >400,

CAN I define patterns at >00 thru >300? which correlates to patterns 0-96 which is another 24 sets of double sized sprite patterns?

If I can do this I have more then enough space for the multiple images I want and can just update the Sprite Attribute Table for the image, If not I may not have enough space and will have to copy patterns from someplaces outside of >400 thru >800 into that address area.

 

 

I guess I'm going to build a pattern list for my sprites and see how many I really am going to end up with. Maybe someone can chime in and help me understand my addressing issues and discuss what to do if you have more then 32 double sized sprite patterns.

 

John

Link to comment
Share on other sites

This is a how best to do this question more then anything.

 

Ok, I'm using alot of patterns for double sized sprites. If I want to change the image of a sprite is it best the change the pattern address in the ATTRIBUTE TABLE or load in another image into the DESCRIPTOR TABLE.

 

A 16x16 sprite is 32bytes and sounds like alot to move but is there really much of a hit in speed to do this?

 

I'm alittle confused by what address is what,

Pattern Descriptor Table is at >800 and starts with pattern >00

Sprite Descriptor Table is at >400 and starts with pattern >80

 

I'm not sure how much space I have, do I have >800->400 space for sprite patterns which is >400,1024bytes,128 patterns at 4 patterns per sprite =32 sets. I may have 32 sprites but I wont be using 32 different pattern sets so 8-10 will be the same pattern and the Attribute Table for these can point to the same address in the Descriptor Table, RIGHT?

 

If the Sprite Attribute Table is at >300 and the Sprite Descriptor Table is at >400,

CAN I define patterns at >00 thru >300? which correlates to patterns 0-96 which is another 24 sets of double sized sprite patterns?

If I can do this I have more then enough space for the multiple images I want and can just update the Sprite Attribute Table for the image, If not I may not have enough space and will have to copy patterns from someplaces outside of >400 thru >800 into that address area.

 

 

For sprite patterns you have 32*32 bytes (1024 bytes.) They have nothing to do with character defs so do as you please. Depending on you sprite size (8 or 32 bytes) you have either 128 8x8 sprite patterns or 32 16x16 sprite patterns....---> key here is "32" <---. If you only want 8x8 sprites then there are 128 patterns available. if you want 16x16 sprites then you can use 32 patterns. Remember that sprite patterns have nothing to do with character patterns (which is non intuitive if your an XB guy.)

 

 

 

 

 

 

 

I guess I'm going to build a pattern list for my sprites and see how many I really am going to end up with. Maybe someone can chime in and help me understand my addressing issues and discuss what to do if you have more then 32 double sized sprite patterns.

 

John

Link to comment
Share on other sites

This is a how best to do this question more then anything.

 

Ok, I'm using alot of patterns for double sized sprites. If I want to change the image of a sprite is it best the change the pattern address in the ATTRIBUTE TABLE or load in another image into the DESCRIPTOR TABLE.

 

A 16x16 sprite is 32bytes and sounds like alot to move but is there really much of a hit in speed to do this?

 

VDP Register 5 stores the location of the Sprite Attribute Table. This is 32 4-byte long records, one for each sprite.

 

VDP Register 6 stores the location of the Sprite Pattern Table. This is up to 2048 bytes in size, and can be located at any 2k interval in VDP. You can even overlap it with the character pattern table, which is what is done in TI Extended BASIC. That means you can have up to 64 sprite patterns of 16x16 size if you want.

 

If you have all your sprite patterns in a single table, just change the attribute table. If you have a few spare patterns you're swapping out, you can either have a rotating pattern that you change directly, or you can have a second sprite pattern table, which you can change to with a single register write.

 

Writing 32 bytes isn't that much of a hit, unless you do a lot of them in a "real-time" moment, like between video frames.

 

I'm alittle confused by what address is what,

Pattern Descriptor Table is at >800 and starts with pattern >00

Sprite Descriptor Table is at >400 and starts with pattern >80

 

I'm not sure how much space I have, do I have >800->400 space for sprite patterns which is >400,1024bytes,128 patterns at 4 patterns per sprite =32 sets. I may have 32 sprites but I wont be using 32 different pattern sets so 8-10 will be the same pattern and the Attribute Table for these can point to the same address in the Descriptor Table, RIGHT?

 

If the Sprite Attribute Table is at >300 and the Sprite Descriptor Table is at >400,

CAN I define patterns at >00 thru >300? which correlates to patterns 0-96 which is another 24 sets of double sized sprite patterns?

If I can do this I have more then enough space for the multiple images I want and can just update the Sprite Attribute Table for the image, If not I may not have enough space and will have to copy patterns from someplaces outside of >400 thru >800 into that address area.

 

I guess I'm going to build a pattern list for my sprites and see how many I really am going to end up with. Maybe someone can chime in and help me understand my addressing issues and discuss what to do if you have more then 32 double sized sprite patterns.

 

It sounds like whatever source you were using (Molesworth?) failed to mention that the sprite pattern table does NOT need to overlay the character pattern table. Read over the TI Tech Page section on the VDP chip, that should give you a good background on the technical:

 

http://nouspikel.group.shef.ac.uk//ti99/tms9918a.htm

 

Adamantyr

Link to comment
Share on other sites

My Molesworth book doesn't have sprites in it, this came from the Editor Assemble Manual and it is restated in Beginning Assembly for the TI pdf here My link Which I really like.

 

This is copied from the Manual,

 

"In the Editor/Assembler, the Sprite Attribute List starts at address >0300. If you

wish to use automatic motion, the Sprite Attribute List must start at that address.

If you put the default base address (>0000) in VDP Register 6, the Sprite Descriptor

Table (described in Section 21.6.2) starts at address >0000. Since the area >0000

through >03FF is used for the Screen Image Table, Color Table, and Sprite Attribute

List, character codes starting at >80, at address >0400, are then normally used for

sprites. When you use sprite motion, only the character codes from >80 through >EF

can be used because the Sprite Motion Table starts at address >0780."

 

So you can see it is not real clear but does say that SAT is at >300 and SDT is at >400. And that if you use sprite motion, which I don't, that SAT has to be at >300

 

the other book says it's best to put the tables at >300 and >400 but never really says why. I took it they were just repeating what the manual says.

Link to comment
Share on other sites

All the table locations in VRAM are arbitrary. Any specific locations you read about only matter if you are working with a specific environment, like if you are interfacing assembly with XB, or trying to use the sprite motion functionality. Otherwise, you can set the tables up however you like.

 

The VDP uses the tables to read the information it needs to display. The tables locations are determined by several of the VDP's write only registers. The location of each table, and where in memory it can be located is directly related to how these table "locations" are used to make VRAM addresses.

 

The VDP can address 16K, so it needs a 14-bit address 2^14 = 16384:

 

Bit:     13   12   11   10    9    8    7    6    5    4    3    2    1    0
Value: |8192|4096|2048|1024| 512| 256| 128|  64|  32|  16|  8 |  4 |  2 |  1 |
TI-Bit:   0    1    2    3    4    5    6    7    8    9   10   11   12   13

 

Remember, TI numbered their bits backwards from the rest of the universe, at least during the 9900 era. Anyway, if you look at the VDP registers and how the table locations are "calculated", you will see, for example that the sprite attribute table (SAT) comes from 7 bits in VDP register 5, and the sprite pattern generator table base address (SPGT) comes from only 3 bits in VDP register 6:

 

       0 1 2 3 4 5 6 7
      +-+-+-+-+-+-+-+-+
REG 5: |0|    S A T    |
      +-+-+-+-+-+-+-+-+

      +-+-+-+-+-+-+-+-+
REG 6: |0|0|0|0|0|SPGT |
      +-+-+-+-+-+-+-+-+

 

So, when the VDP is looking for the Y, X, NAME, and COLOR of a sprite, it needs to build a 14-bit address in the VDP RAM to get the 4 bytes needed. First the VDP takes the 7 bits from REG-5 and uses those as the top 7 bits in the 14-bit address:

 

                1 2 3 4 5 6 7 8 9 A B C D E
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
14-bit address: |    S A T    |X X X X X X X|
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+

 

That means that a value of 1 (0000001 in binary) in REG-5 will translate to the top 14-bits of the address, in which case it becomes: 0000001|0000000 minimum, or 128 decimal and >80 hex. A value of 2 (0000010) will be 0000010|0000000 or 256. 3 would be 0000011|0000000 or 384, etc. So the SAT can only be found on a 128 byte "boundary" because of where the bits from REG-5 are used to build the address to find the SAT. The lower 7 bits represent 128 bytes, which are the bytes that make up the sprite attributes, i.e. 4 bytes per sprite (Y, X, NAME, and COLOR), and there are 32 sprites, so 4 * 32 = 128.

 

Now, the SPGT is located from only 3 bits from REG-6, like this:

 

                1 2 3 4 5 6 7 8 9 A B C D E
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
14-bit address: |SPGT |X X X X X X X X X X X|
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+

 

Since there are only 3 bits, and they are used to form the TOP 3 most significant bits of the 14-bit address, the table can only be located on boundaries that are multiples of 001|00000000000 or 2048 bytes, or >800 hex.

 

And so it goes for each of the tables in the VDP RAM. Each tables uses a different number of bits from various registers to form the top most bits of the 14-bit address being derived by the VDP. Thus, you can put the tables anywhere you want, but overlapping certain tables will cause you problems if you want things to work as expected. However, the regular tile pattern table and the sprite pattern generator table are two that can be overlapped if you want to use the same patterns for your sprites as you do for tiles (aka characters.)

 

Thus, because environments like XB expect these tables to be set up in certain locations, if you need to work with such an environment, you have to find out where XB (or whatever environment you are working in) expects these tables to be. Also, keep in mind that the VDP registers are WRITE ONLY, so you cannot find out where the tables are currently located, you can only write over them or know they are in a specific location because some other environment set them up that way (like the console start-up routines or XB).

 

I hope this makes sense.

 

Matthew

Link to comment
Share on other sites

As an example, here's the VDP mapping for my CRPG:

 

Address				Contents
Start		End		
0	0000	255	00FF	Pattern Table
256	0100	511	01FF	
512	0200	767	02FF	
768	0300	1023	03FF	
1024	0400	1279	04FF	
1280	0500	1535	05FF	
1536	0600	1791	06FF	
1792	0700	2047	07FF	
2048	0800	2303	08FF	Screen Table
2304	0900	2559	09FF	
2560	0A00	2815	0AFF	
2816	0B00	3071	0BFF	Sprite Attribute Table
3072	0C00	3327	0CFF	Buffer Screen Table
3328	0D00	3583	0DFF	
3584	0E00	3839	0EFF	
3840	0F00	4095	0FFF	Character Graphic Data
4096	1000	4351	10FF	File Buffers
4352	1100	4607	11FF	
4608	1200	4863	12FF	PAB Space
4864	1300	5119	13FF	
5120	1400	5375	14FF	
5376	1500	5631	15FF	
5632	1600	5887	16FF	Save Game Data
5888	1700	6143	17FF	
6144	1800	6399	18FF	Sound Data
6400	1900	6655	19FF	
6656	1A00	6911	1AFF	
6912	1B00	7167	1BFF	
7168	1C00	7423	1CFF	
7424	1D00	7679	1DFF	
7680	1E00	7935	1EFF	
7936	1F00	8191	1FFF	
8192	2000	8447	20FF	Color Table
8448	2100	8703	21FF	
8704	2200	8959	22FF	
8960	2300	9215	23FF	
9216	2400	9471	24FF	
9472	2500	9727	25FF	
9728	2600	9983	26FF	
9984	2700	10239	27FF	
10240	2800	10495	28FF	Sprite Pattern Table
10496	2900	10751	29FF	
10752	2A00	11007	2AFF	
11008	2B00	11263	2BFF	
11264	2C00	11519	2CFF	
11520	2D00	11775	2DFF	
11776	2E00	12031	2EFF	
12032	2F00	12287	2FFF	
12288	3000	12543	30FF	Sprite Pattern Table #2
12544	3100	12799	31FF	
12800	3200	13055	32FF	
13056	3300	13311	33FF	
13312	3400	13567	34FF	
13568	3500	13823	35FF	
13824	3600	14079	36FF	
14080	3700	14335	37FF	
14336	3800	14591	38FF	Mob Graphics
14592	3900	14847	39FF	
14848	3A00	15103	3AFF	
15104	3B00	15359	3BFF	Reserved for FDC
15360	3C00	15615	3CFF	
15616	3D00	15871	3DFF	
15872	3E00	16127	3EFF	
16128	3F00	16383	3FFF

 

It looks a lot better in the Excel spreadsheet. :)

 

Adamantyr

Link to comment
Share on other sites

Well, I thought I had picked me a simple game to program for my first assembly project, but graphics wise has ended up being alot more then I thought.

Background there is only 1 character, but sprite wise I need 30, 16x16 sprites so that 120 patterns. That fits into my current model with the SDT at >400 so I'll keep it there for now.

 

Hopefully, after this project I'll have a much better understanding for the next one.

 

Thanks for the info about the table locations, I knew stuff could be moved around but all the sources I have been reading from set them in the same but they did mention that they could be located in other places. Actually there is so much new information running around in my head from registers and addresses to pattern tables and vdp bits, that it's really hazy at this point. I know at some point it will start coming together.

 

I started writing a question on here a few days ago and the longer I wrote it and reasoned out what I didn't know the more things clicked and started making sense. I didn't need to post the question because I had the information but just hadn't been able to put it together until I laid it out in a question and started discussing it with myself, lol.

 

John

Link to comment
Share on other sites

Hey guys,

When defining a pattern like this one,

BIN2 DATA >C4C4,>0000,>0000,>0000

DATA >0000,>0000,>0000,>0000

DATA >2323,>0000,>0000,>0000

DATA >0000,>0000,>0000,>0000

 

the last row is all 0's, can it be left out of the code?

even better the 3rd row is 3/4 0's so if this the same thing.

BIN2 DATA >C4C4,>0000,>0000,>0000

DATA >0000,>0000,>0000,>0000

DATA >2323

 

All the bits are off so no need to record them as off when the next pattern is going to jump the address down 32 bytes and write in that location?

Link to comment
Share on other sites

It does not work like that. What you are talking about would be a form of data compression. When you are setting up your patterns, you are doing block moves from CPU RAM/ROM to the VRAM. If you need to define a 16x16 sprite, you will have to send all 32 bytes of the pattern. You can not rely on the VRAM being all zero either.

 

The easiest way to set up the block moves is to use fixed sizes blocks of data. Otherwise you could devise some way to represent repeating patterns, and the number of times it repeats, then write your VRAM loading routine to recognize the format and load the uncompressed data. But, >0000 is just as valid as data to the computer as >5A5A, it is just us humans who see it differently.

 

Have a look at RLE (run length encoding), I think that is a simple form of compression similar to what I was suggesting.

 

Matthew

Link to comment
Share on other sites

hey Adamantyr, I missed your last post there, that's great to see, thank you.

 

Thanks Matthew, I was entering them all in fully, just thought I might be able to save some time/space by skipping a few lines if it wasn't needed. That makes sense that the space may not be all 0's something else could be written in there before I send new data to it, and that would be a real pain in the ass to trouble shoot too.

Link to comment
Share on other sites

hey Adamantyr, I missed your last post there, that's great to see, thank you.

 

Thanks Matthew, I was entering them all in fully, just thought I might be able to save some time/space by skipping a few lines if it wasn't needed. That makes sense that the space may not be all 0's something else could be written in there before I send new data to it, and that would be a real pain in the ass to trouble shoot too.

 

No problem. Ask me any questions you have on how the layout was achieved. There's actually quite a bit of space in VDP memory for a lot of things, as long as you're not doing full bitmap mode. (Which consumes nearly all of it.)

 

Yeah, I can see why you thought leaving off zeroes would work... after all, you can do it in TI BASIC. :) But the compiler's a different beast. It has to have everything literally spelled out. On the plus side, it is a heavy ambrosia indeed, to realize that you have COMPLETE control over nearly everything... a welcome change from BASIC where you had to work around things like it using floating point for everything, running interrupts all the time, using most of VDP as stack space for strings, etc.

 

Adamantyr

Link to comment
Share on other sites

Hey guys,

When defining a pattern like this one,

BIN2 DATA >C4C4,>0000,>0000,>0000

DATA >0000,>0000,>0000,>0000

DATA >2323,>0000,>0000,>0000

DATA >0000,>0000,>0000,>0000

 

the last row is all 0's, can it be left out of the code?

even better the 3rd row is 3/4 0's so if this the same thing.

BIN2 DATA >C4C4,>0000,>0000,>0000

DATA >0000,>0000,>0000,>0000

DATA >2323

 

All the bits are off so no need to record them as off when the next pattern is going to jump the address down 32 bytes and write in that location?

 

 

This would most definitely work provided you zero'd out the Sprite descriptor table before hand. It's actually a good idea for saving code space if you have a lot of patterns that have trailing zero's. If you prepend each data block with a byte describing how many bytes to move for each pattern your golden. It's only the zero bytes that are within the character definition that would have to be represented. Way to think out of the box!

Link to comment
Share on other sites

Yes, but your programming of the patterns gets a lot more complicated too. You have to also specify the starting VRAM address of each pattern, since the VDP auto-increment counter will no longer be in the correct location for continuous pattern loading. Instead of specifying how many bytes to write, it would probably be easier to specify how many trailing >00 bytes there are, and always write the same number of bytes for pattern (32 bytes for a 16x16 sprite). Say you had a number of >00 bytes set to 10, then you would write 32 - 10, or 22 bytes which is data, then loop 10 times writing >00. Then do the next pattern.

 

Anyway, there are a lot of different ways to do this, but any kind of "compression" has a trade off in complexity and set-up time (which may not matter if it happens once during initialization.) If your patterns are highly compressible, it might be wroth it.

 

Matthew

Link to comment
Share on other sites

"provided you zero'd out the Sprite descriptor table before hand" - I figured that the memory location would all be 0's on bootup, but then after Matthews comments it made me think. What if I later in the program I loaded a shorthanded pattern over a place in the table where a full pattern was. Then I'ld have garbled patterns. I guess a small routine to clear the 32bytes before writing the new pattern to the spot would be easy to make.

 

I guess it's something to keep in mind If I ever want to make some kind of massive program, but I have no need for that kind of compression right now. I was only trying save some entry time. I'll have to rewrite my sprite editor to output assembly code now, need to do some work on that again anyway.

Link to comment
Share on other sites

I looked into using RLE for character patterns on the TI once... it turned out that any framework for supporting it took up more space than the uncompressed graphics! The problem is that character patterns are highly chaotic most of the time... RLE relies on large blocks of identical data to be truly effective. Color data would probably be more useful to use RLE with.

 

However, probably the least-pain method of getting your graphics and color is to just output them to disk as memory image files and load them in. That necessitates writing up an external editor and a bit of overhead at the start during initialization, but it's well worth it. Having data in the CPU RAM that just gets copied into VDP is a waste!

 

Adamantyr

Link to comment
Share on other sites

I was only trying save some entry time.

BIN2    DATA >C4C4,>0000,>0000,>0000
       DATA >0000,>0000,>0000,>0000
       DATA >2323,>0000,>0000,>0000
       DATA >0000,>0000,>0000,>0000

I think something like this should be okay ...

 

BIN2    DATA >C4C4,0,0,0
       DATA 0,0,0,0
       DATA >2323,0,0,0
       DATA 0,0,0,0

;)

 

 

 

Hmmmmm. I think you nailed it ;-) Pays to read the whole post huh ?

Link to comment
Share on other sites

  • 4 weeks later...

As an example, here's the VDP mapping for my CRPG:

 

Address				Contents
Start		End		
0	0000	255	00FF	Pattern Table
256	0100	511	01FF	
512	0200	767	02FF	
768	0300	1023	03FF	
1024	0400	1279	04FF	
1280	0500	1535	05FF	
1536	0600	1791	06FF	
1792	0700	2047	07FF	
2048	0800	2303	08FF	Screen Table
2304	0900	2559	09FF	
2560	0A00	2815	0AFF	
2816	0B00	3071	0BFF	Sprite Attribute Table
3072	0C00	3327	0CFF	Buffer Screen Table
3328	0D00	3583	0DFF	
3584	0E00	3839	0EFF	
3840	0F00	4095	0FFF	Character Graphic Data
4096	1000	4351	10FF	File Buffers
4352	1100	4607	11FF	
4608	1200	4863	12FF	PAB Space
4864	1300	5119	13FF	
5120	1400	5375	14FF	
5376	1500	5631	15FF	
5632	1600	5887	16FF	Save Game Data
5888	1700	6143	17FF	
6144	1800	6399	18FF	Sound Data
6400	1900	6655	19FF	
6656	1A00	6911	1AFF	
6912	1B00	7167	1BFF	
7168	1C00	7423	1CFF	
7424	1D00	7679	1DFF	
7680	1E00	7935	1EFF	
7936	1F00	8191	1FFF	
8192	2000	8447	20FF	Color Table
8448	2100	8703	21FF	
8704	2200	8959	22FF	
8960	2300	9215	23FF	
9216	2400	9471	24FF	
9472	2500	9727	25FF	
9728	2600	9983	26FF	
9984	2700	10239	27FF	
10240	2800	10495	28FF	Sprite Pattern Table
10496	2900	10751	29FF	
10752	2A00	11007	2AFF	
11008	2B00	11263	2BFF	
11264	2C00	11519	2CFF	
11520	2D00	11775	2DFF	
11776	2E00	12031	2EFF	
12032	2F00	12287	2FFF	
12288	3000	12543	30FF	Sprite Pattern Table #2
12544	3100	12799	31FF	
12800	3200	13055	32FF	
13056	3300	13311	33FF	
13312	3400	13567	34FF	
13568	3500	13823	35FF	
13824	3600	14079	36FF	
14080	3700	14335	37FF	
14336	3800	14591	38FF	Mob Graphics
14592	3900	14847	39FF	
14848	3A00	15103	3AFF	
15104	3B00	15359	3BFF	Reserved for FDC
15360	3C00	15615	3CFF	
15616	3D00	15871	3DFF	
15872	3E00	16127	3EFF	
16128	3F00	16383	3FFF

 

It looks a lot better in the Excel spreadsheet. :)

 

Adamantyr

 

 

Hey Adam you have screen table and buffer screen table, so are you double buffering your screen output?

Link to comment
Share on other sites

Hey Adam you have screen table and buffer screen table, so are you double buffering your screen output?

 

Yes, I have several cases where it just rebuilds the entire screen in a buffer and then swaps to it. That lets me update the screen without any rips, tears, or corrections needed.

 

Classic example: You have a number value that has multiple significant digits. The value drops from a four-digit value to three. but unless you are adding trailing spaces to your string output, it will not wipe out the fourth digit and you have a misrepresented value.

 

Adamantyr

Link to comment
Share on other sites

when you swap to it, do you just redefine the Screen Table and it redraws the screen based on what is stored in the new addresses?

 

Exactly. It's a zero-sum transition because on every VDP cycle, it's just reading that register and writing to the screen whatever it finds there.

 

Adamantyr

 

 

That's nice!

 

Ok, when using more then one pattern table. when you switch the the 2nd table, I'm guessing any patterns you want to keep the same has to be in both pattern tables?

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...