It is currently Fri Oct 20, 2017 4:43 pm

All times are UTC - 7 hours

Forum rules


Post new topic Reply to topic  [ 27 posts ]  Go to page Previous  1, 2
Author Message
PostPosted: Tue Feb 23, 2016 9:12 am 

Joined: Fri Jul 04, 2014 9:31 pm
Posts: 786
93143 wrote:
New map data for both players has been properly compiled and interleaved in WRAM (ie: a miracle happens)

It might be possible to do this manually on the S-CPU, but it would take a huge chunk of each frame*. It might work, as long as not much is happening (no AI racers?).

But if a special chip, such as the SA-1, were being used, it seems like it would be possible to build a 4 KB DMA-ready chunk of map quite rapidly, in maybe 6% of a frame or so. This number seems to be similar on the SA-1 and the Super FX, if I've understood things correctly. The SA-1 can use repeated DMA from ROM to BW-RAM to build the map out of 64-byte strips, and the Super FX should be able to use cached code to pipeline the data through the ROM and RAM buffers so as to saturate both.

Adding an SA-1 or Super FX seems like overkill for just hacking a split-screen two-player mode into F-Zero (which would probably be a substantial task on its own). It would probably be more appropriate in the context of a whole new game designed to use the full power of the expansion chip (which would be a monumental task, not to mention the copyright issue - but boy do I have ideas...).

*Block move is so freaking useless on the 65816; unrolled 16-bit indexed long -> indexed absolute is faster. And of course if HDMA is running, which it is, I can't carelessly use DMA because of that rev.1 CPU bug...

EDIT: I may have made an unwarranted assumption. This post and the next one should be taken with a grain of salt, if you can make sense of them at all.

Last edited by 93143 on Wed Feb 24, 2016 2:06 am, edited 1 time in total.

PostPosted: Tue Feb 23, 2016 6:03 pm 

Joined: Fri Jul 04, 2014 9:31 pm
Posts: 786
Wait a minute. If one were to completely turn off HDMA during backdrop/sky areas (so any gradients would have to be paletted), it would be possible to use DMA during that time without risking a deadlock on a rev.1 CPU. And if my math is right, doing this with DMA would take a little over 32 scanlines in total. So with a roughly Super Mario Kart-sized (20 scanlines) backdrop/sky in each player window, it might actually work, as long as the first player's map position can be determined early enough to get the first half started in time. I think the IRQ structure makes sense...

PostPosted: Tue Feb 23, 2016 6:15 pm 
User avatar

Joined: Mon Sep 15, 2014 4:35 pm
Posts: 3074
Location: Nacogdoches, Texas
I'm listening, but I have no clue what's going on... :lol: What are we transferring with DMA? It can't be the Mode 7 tilemap, because that's in vram, unless you're wanting to shift this around in ram. Oh yeah, didn't you say it was because of how you wanted to transfer data from the ram on a cart using an expansion chip? Why is this chip even needed again? Sorry... :lol:

PostPosted: Wed Feb 24, 2016 12:49 am 

Joined: Fri Jul 04, 2014 9:31 pm
Posts: 786
The problem is that while F-Zero as it stands can simply construct and DMA a couple of rows/columns of the tilemap on demand, switching out 4 KB of map data each frame is considerably more labour-intensive. I'm attempting to brainstorm methods of building a chunk of data in RAM (WRAM or BWRAM or whatever) that can simply be DMAed to VRAM in one shot during VBlank.

Actually, the numbers I've given are (a) probably wrong, and (b) for pulling from a giant uncompressed map in ROM, which is not how F-Zero works. I imagine a hack could work that way in principle, with a massive ROM expansion, but even then you're pulling non-contiguous 64-byte pieces and concatenating them, which is a lot of DMA transfers, meaning a lot of overhead. It would require some extra code for DMAing across bank boundaries (indexing works, but DMA wraps) that would increase the overhead a little beyond what I've suggested above. I still think it could probably be forced to work on a stock SNES, but an enhancement chip would help quite a bit.

I should probably study up on how F-Zero builds its maps before I get too much farther along on this topic...

PostPosted: Mon Sep 12, 2016 3:52 am 

Joined: Sat Aug 06, 2016 10:22 pm
Posts: 15
Hey all, just wanted to post some information on F-Zero VS, the server-based multiplayer version of SNES F-Zero. Basically what FZVS is for those who don't know is a 4-player multiplayer version of SNES F-Zero that uses a modified version of snes9x to allow people to connect via a server. The author of it stated,
"During the emulation process, many patches to the ROM are made to turn various aspects of the the gameplay into multiplayer, while several memory hooks watch the RAM for certain signals that indicate different game states etc."
You can read more about that exact process as this page ( at the FZVS blog (long abandoned).

The author got asked a lot if this could work on real hardware, either with an XBAND or via a link cable between systems. He said it was definitely possible, his code would just have to be tinkered with a little. Good news is that he uploaded the source code of the project (in two parts, the snes9x code and the server code) at his blog:

Bad news is the link to the source code download no longer works and I can't find a working link anywhere across the internet. None of the previous links work in either. Basically this source code is lost. The only other thing I've thought of trying is sending emails to the commenters on the source code page who did indeed download it and see if they still have it and can upload it somewhere like github or something (however, a lot of them, don't look active anymore). I also found a blog that linked to the source code, but that person hasn't wrote a article on the blog in years so who knows if they would respond to an email.

I wouldn't be able to work on this even if I had the source code, but I'd like the information out there for anyone in the future who would ever want to give it a try. This seems to me to be a good project for the SNESoIP team to work on (ttp:// ... tendo.html) or to get working on real hardware via link cable. I found an old thread on the old version on via that discusses the feasibility of making a link cable.,6893.0.html
Here's what the author had to say,
"It wouldn't require a lot of code modification, although I'd need to do some things differently. I'd need to inject some code into the main loop to do what the emulator essentially does (updating the RAM etc). But I also patch the ROM in realtime, so in these cases I'd probably need to substitute it with different approach. All possible of course."

User neviksti wrote about the possibility of making a joyport link cable (sorry this is a lot of text, can't find spoiler tags?),
"Also, with the joyport link cable, you wouldn't have to worry about latency issues.

fzvs, how are you currently dealing with latency issues? (also, what about other synchronizing issues ... like if a car explodes?)

Anyway, here are the cable details:

SNES controller connector:
rounded tip
square end

The SNES can read the values on IO,D0,D1.
Clk is an output from the SNES that is pulsed when reading certain SNES hardware registers.
The SNES can output values on IO and latch.

Unfortunately, all the SNES "extender" cables I've found on Ebay actually only work for controllers (not the guns, etc.). That is because they are cheap and only pass through Gnd,Vcc, Clk,latch,D0.

So the very simplest design would be take two of these extenders and:
D0 <- latch
latch -> D0

The problem with this is there is no easy way to know when the next bit is ready. Carefully written code can make this work though.

If all lines pass through (buy two of those cheap TribalTaps and gut them for the cable connectors):
GND <--> GND
D0 <- latch
D1 <- IO
latch -> D0
IO -> D1

Or, you could even make it asymmetric so the snes can detect which side it is, and use this to automatically select one machine as "player 1". You can either use careful code and send twice as much now, or use one of the data lines to indicate when the next bit is ready (easier to code and debug).

If the first way works, that is clearly preferable, as it would be the easiest and cheapest for anyone to make. Also, the extenders are usually 6 foot, so after splicing two to make the cable it would be 12 foot ... which would be a decent distance for setting up the two systems. You could instead make the SNES connectors attach to phone line connectors (most phone lines have at least 4 wires internally)... then you could string a long phone line to set the distance however you want."

I know some people want to work on a split screen version of F-Zero, but I think a link cable version would be great too and wouldn't lessen the gameplay by having less view-able driving area on the screen. The author of FZVS only ever made it v1.1 before he disappeared. In v1.2, he was going to correct the cars crash behavior (in the original game your momentum is transferred to the car you hit). He was also working on a never released program that looked at debugging SNES games in an unusual, but interesting way: SNES Code Visualizer. Below I will post a bunch of code that the original F-Zero author posted on his blog so that in case it ever disappears off his site it will still be on this thread; hopefully it can be useful to someone in the future who would ever want to work on this:

Now I will explain exactly what happens inside the SNES emulator when you load the ROM (only the ROM specific stuff needs to be mentioned):

1) To disable any menu selections at the title screen other than the first option, patch 03:8176 INC to NOP:
Memory.ROM[(0x8000*3)+0x0176] = 0xEA;

2) To disable the 'demo race' at title screen (as this will trigger game states we don't need), patch 03:8143 BNE $8148 to BRA $8148:
Memory.ROM[(0x8000*3)+0x0143] = 0x80;

3) Then we wait for the player to select a car. To catch car selection, check memory status at $7E:0055. When it equals 0x03 we know a car has been selected. You can determine which car was selected by reading $7E:005A at this point. The order (from 0-3) is bf (blue falcon), gf (golden fox), wg (wild goose), fs (fire stingray). This is a different representation to what is used internally in the F-Zero ROM, where it is bf, wg, gf, fs. So I change the returned value to suit that.

4) We should not proceed to the game until all players have entered their car selections. So, we wait for the game to start transitioning to the next screen, breaking when $7E:0055 == 0x05. When this condition is met, we halt the screen progression until we receive notification from the server that everyone is ready. In order to pause the game while keeping the music playing etc, we patch the ROM 03/851F: A5 55 F0 74 LDA $55 to become 5C 1F 85 03 JMP 03851F.
Memory.ROM[(0x8000*3)+0x051F] = 0x5C;
Memory.ROM[(0x8000*3)+0x0520] = 0x1F;
Memory.ROM[(0x8000*3)+0x0521] = 0x85;
Memory.ROM[(0x8000*3)+0x0522] = 0x03;

5) We only continue when we have received notification from the server that all the players have selected their cars. Furthermore the server will have sent other data such as the track to race on, player id, number of players in the race etc.

6) Then we set the car types based on the data received from the server. The player previously received from the server a player id, this is a unique value from 0 to 3. It determines the players position in the starting line from left to right.

So, let's assume we are the first player to log on to the server, making our player id == 0. This means, our car will be in the fs position on the starting line, on the far left. However, let's assume we chose bf as our car.

We need to change the viewpoint of the car to match the fs location. To do this, we patch 00:D2EF A5 52 LDA $52 to A9 0x LDA #$x where x is a value from the internal representation of the car selection (being bf, wg, gf, fs):
Memory.ROM[0xD2EF-0x8000] = 0xA9;
Memory.ROM[0xD2F0-0x8000] = x;

To set the player car palette to be based on the player id, patch 00:D72B LDA $52 to LDA #$pid where pid is the player id:
Memory.ROM[0xD72B-0x8000] = 0xA9;
Memory.ROM[0xD72C-0x8000] = pid;

Then we can set the opponent car types, based on the data we received earlier. To do this, we write to RAM:
Memory.RAM[0x1133] = x;
Memory.RAM[0x1135] = y;
Memory.RAM[0x1137] = z;

where x, y and z are the player car types from right to left on the starting line, excluding the player id's location. So in our example of having player id == 0, with the starting line order being (fs, bf, wg, gf) the code would be:
Memory.RAM[0x1133] = player who is in the gf position;
Memory.RAM[0x1135] = player who is in the wg position;
Memory.RAM[0x1137] = player who is in the bf position;

Then we can set the colours of the opponent cars. Our aim is to make sure that the first car is always pink, second is always blue, third is green and fourth is yellow - no matter what car type is selected. We write to RAM at 0x0C41, 0x0C43, 0x0C45, 0x0C47 where 0x0C41 is the player car's colour, and the 0x0C43/5/7 are the colours of the opponents based on the player id. So, if we are in player id position 0:
Memory.RAM[0x0C41] = 0x0E;
Memory.RAM[0x0C43] = 0x08;
Memory.RAM[0x0C45] = 0x0A;
Memory.RAM[0x0C47] = 0x0C;

Finally we then make a few small patches that are needed to make things run smoothly. We patch the code at 00:D486 LDX #$00 to JMP $D4A7, to skip the car loop iteration:
Memory.ROM[0xD486-0x8000] = 0x4C;
Memory.ROM[0xD487-0x8000] = 0xA7;
Memory.ROM[0xD488-0x8000] = 0xD4;

we patch the code at 00:D32B STA $1131,X to NOPs, to make sure the car types are never overwritten:
Memory.ROM[0xD32B-0x8000] = 0xEA;
Memory.ROM[0xD32C-0x8000] = 0xEA;
Memory.ROM[0xD32D-0x8000] = 0xEA;

and to stop the player car palette from defaulting back to the original when it crosses the finish line, patch 00:8DB6 JSR $C782 to NOPs:
Memory.ROM[0x8DB6 - 0x8000] = 0xEA;
Memory.ROM[0x8DB7 - 0x8000] = 0xEA;
Memory.ROM[0x8DB8 - 0x8000] = 0xEA;

8) Now I need to explain how the F-Zero game actually works.

F-Zero tracks the progress of five displayed cars. The locations of these cars are stored at: 7E:0B70-0B79 (x values) and 7E:0B90-0B99 (y values). Since each race has something like 20 cars or more, there are many cars on the track whose locations are unaccounted for at any one time. When these cars need to be displayed, the game 'places' them on the track where it thinks they should be, based on a checkpoint system, rather than actually racing them around the track properly. This was probably done due to SNES system constraints, but discovering this answered a lot of questions for me. Ever wonder why there was always a car right behind you, no matter how well you were driving? Now you know :)

What this means, is we need to make a few more modifications.

We need to get rid of the annoying opponent 'catch up' code. As I stated above, the game often decides when a car is about to over take you. It's not like the cars are always racing around the track in a linear fashion. The game may decide to make a car jump from really far away, to just behind you, simply because you are playing poorly. We can't have random 'check' warning messages in our multiplayer races either.

We also need to get rid of all the generic enemy cars, these are the ones that have the boring racing stripe and never win the races. If we don't do this, the code will just keep introducing them in to the game everytime we crash in to the wall a few times.

The good news is we can solve both problems simply. We patch 00:DDFC JSR $DED0 to EA NOP * 3:
Memory.ROM[0xDDFC - 0x8000] = 0xEA;
Memory.ROM[0xDDFD - 0x8000] = 0xEA;
Memory.ROM[0xDDFE - 0x8000] = 0xEA;

9) Next we have to stop the opponent AI from working, while still allowing the cars movement. If we don't do this, then the car will have jagged motion around the track as it is receiving contradicting movement commands from the server and the AI. So, we patch 00:DDDA JSR $DE57 to EA NOP * 3:
Memory.ROM[0xDDDA - 0x8000] = 0xEA;
Memory.ROM[0xDDDB - 0x8000] = 0xEA;
Memory.ROM[0xDDDC - 0x8000] = 0xEA;

10) Now another thing you will notice is that when the race first starts, even though you have disabled the opponents AI, they still boost off the finish line (before slowing to a halt). If we don't stop this boost, the car will have the same jagged motion described earlier at the start of the race. So we patch 00:8D3E LDA #$02 to LDA #$00:
Memory.ROM[0x8D3F - 0x8000] = 0x00;

11) The race relies on synchronization, and if a player accidentally pauses the game they will break the synchronization. So we disable pausing by patching 00:C8FF F0 06 BEQ $C907 to be 80 06 BRA $C907:
Memory.ROM[0xC8FF - 0x8000] = 0x80;

12) Now we must make sure that when the race is complete, the game does not proceed beyond the race time summary screen until it receives notification from the server that all players have finished. We patch 00:CD84 90 03 BCC $CD89 to be 80 03 BRA $CD89:
Memory.ROM[0xCD84 - 0x8000] = 0x80;

13) And finally, we can resume execution of the ROM (from point 4 above) meaning the game will progress beyond the car select screen to the league selection:
Memory.ROM[(0x8000*3)+0x051F] = 0xA5;
Memory.ROM[(0x8000*3)+0x0520] = 0x55;
Memory.ROM[(0x8000*3)+0x0521] = 0xF0;
Memory.ROM[(0x8000*3)+0x0522] = 0x74;

14) We skip the league and difficulty selection because it is already chosen for us by the server. I do this crudely by patching 03/8795: B0 35 BCS $87CC to BRA $87CC 80 35:
Memory.ROM[(0x8000*3)+0x0795] = 0x80;

and by patching 03/87E6: 6B RTL to be another INC $55 (this makes it 7, to start the race). Note this pushes the RTL into the 'class' select code but that code is not used anyway:
Memory.ROM[(0x8000*3)+0x07E6] = 0xE6;
Memory.ROM[(0x8000*3)+0x07E7] = 0x55;
Memory.ROM[(0x8000*3)+0x07E8] = 0x6B;

15) We instead set the league and the track number by patching the RAM at 7E:0053 (track number) and 7E:005A (league number).
Memory.RAM[0x53] = track_num;
Memory.RAM[0x5A] = league_num;

16) By this stage the game is preparing to start the race. All players need to send their location and orientation to the server before the race starts, otherwise the opponent cars will temporarily disappear as the server has not been told where they are. We wait for the locations to be loaded by breaking when 7E:0054 == 2, 7E:0055 == 0 and 7E:0056 == 2:
if((Memory.RAM[0x54] == 0x02) && (Memory.RAM[0x55] == 0x00) && (Memory.RAM[0x56] == 0x02))

then we read the 16 bit value at 7E:0B70 (this means also 7E:0B71) to get the x value, and similarly 7E:0B90/1 for the y value. The car's orientation is at 7E:0BD1.

17) Once the server has received all the data required, it can instruct the clients to start the race. Each emulator must now send the player's location to the server, while receiving and updating the opponents locations from the server as fast as possible. It should be obvious that the opponent x and y location values are stored in RAM at 7E:0B72/3,7E:0B74/5, 7E:0B76/7 and 7E:0B92/3,7E:0B94/5, 7E:0B96/7. Similarly the opponent orientation data is at 7E:0BD3, 7E:0BD5 and 7E:0BD7 where the order is based on the player id.

18) We want to be able to tell the server when our player has finished the race, either by crossing the finish line or destroying the car. So we watch the RAM at 7E:0054 until it equals 3:
if(Memory.RAM[0x54] == 0x03) game_state = GAMESTATE_RACEFINISHED;

19) And there is just one tiny other detail. There is a fifth car. Remember above, I said the AI controls 5 cars at any one time? Well this fifth car is a generic car and we need to remove it. I do it the laziest way possible, I just constantly reset it's location to zero every vsync.
Memory.RAM[0x0B78] = 0;
Memory.RAM[0x0B79] = 0;
Memory.RAM[0x0B98] = 0;
Memory.RAM[0x0B99] = 0;


Car collisions will not work properly until we make a few modifications. When a collision between two cars occurs, both emulator instances involved try to control both car rebound velocities. This conflict results in jagged movement.

The solution to this is to:

a) Make sure that each emulator instance only rebounds its own player during a player-opponent collision by patching occurrences of the code 99 20 0B STA $0B20,Y to NOPs at ROM addresses 00:BDB2, 00:BDD5, 00:BDEF and 00:BDFE.

b) Make the player's rebound speed equal to his current speed instead of the opponents speed (which is what happens in the game) by patching occurrences of the code 9D 20 0B STA $0B20,X to NOPs at ROM addresses 00:BDAE, 00:BDCB and 00:BDE5.

Also, looks like he was maybe working on a disassembly of the game (or just research for FZVS):

PostPosted: Mon Sep 12, 2016 10:51 pm 

Joined: Fri Oct 24, 2014 1:56 am
Posts: 75
The author of FZVS is a member here: mikeyz

PostPosted: Wed Sep 14, 2016 11:57 am 

Joined: Sat Aug 06, 2016 10:22 pm
Posts: 15
Unfortunately he joined over 7 years ago and only posted three times. Doesn't even show when his last visit was since it was so long ago.

PostPosted: Wed Sep 14, 2016 1:44 pm 
User avatar

Joined: Sat Jul 12, 2014 3:04 pm
Posts: 936
suFami wrote:
Unfortunately he joined over 7 years ago and only posted three times. Doesn't even show when his last visit was since it was so long ago.

Not necessarily. The "don't show others your online status" checkbox also prevents a logging of a "last visit" (see: my profile)

PostPosted: Sat Nov 12, 2016 2:34 pm 

Joined: Fri Jul 04, 2014 9:31 pm
Posts: 786
suFami wrote:
4-player multiplayer
lessen the gameplay by having less view-able driving area on the screen

I was thinking that if you could get 8 KB of map data decompressed and concatenated in one frame without crowding out the rest of the game engine, you could do a 4-player split-screen F-Zero on SNES.

Unfortunately, the task is probably utterly impossible on the S-CPU, not least because you'd need a big ugly H-IRQ to handle the Mode 7 matrix split. This implies the SA-1, or possibly the Super FX, and it happens that both of those chips draw too much current to be used together with a multitap.

More importantly, I'm pretty sure it's impossible on the S-PPU regardless, since only half the matrix is reapplied every pixel, so you can't do arbitrary rotation changes mid-scanline...

Oh well... it was kinda ugly anyway...

f-zero_4player.jpg [ 68.24 KiB | Viewed 2163 times ]
PostPosted: Mon Nov 14, 2016 12:39 pm 

Joined: Sat Aug 06, 2016 10:22 pm
Posts: 15
Looks good, too bad you're saying it can't work. If you need any info definitely check out CatadorDeLatas's F-zero Notes Document:
He's done a lot of work to map out pretty much the whole game. Tons of RAM and ROM addresses with explanations. I'd say right now CatadorDeLatas knows the inner workings of SNES F-Zero more than anyone else besides the original developers.

He's mostly active in this F-Zero thread on SMWCentral:
He's made patches that make the CPU cars explode if they fall off the track and even gave them health like the player has (!).
Here's a link to a work in progress document he's making on F-Zero's SPC engine: Maybe KungFuKirby could be of some help? Here's what he said about it in the thread,
"I tried modifying EBMusEd (Earthbound Music Editor) to work but I couldn't actually do it. Mainly because EB is HiRom and FZ is LoRom, this screws SNES address to PC address conversion."

He's also made three useful LUA scripts (Checkpoint, Track, and Car info):

PostPosted: Fri Nov 18, 2016 6:14 pm 

Joined: Wed Jul 09, 2008 8:46 pm
Posts: 234
Yup, I fooled around with this game back in 2012.

All I did with F-Zero was to give it the ability to pick any of the course music to race with (which for me acts as a bank modifier). nensondubois produced the sub-tune portion of the music modifier, and what I produced only works at the start of a race if memory serves me right.

Here's the music modifier, combining nensondubois's original code and my bank modifier (for the US version):
(Bank ID at start of race)
The music modifier by nensondubois supports 01-08, wrapping around at 08. The bank modifier, which only affects sub-tune tune 06, supports 00-09. By default, bank 07 is the one that's loaded. The game crashes after bank 09.

PostPosted: Sat Nov 26, 2016 10:41 pm 

Joined: Fri Jul 04, 2014 9:31 pm
Posts: 786
suFami wrote:
Looks good, too bad you're saying it can't work.

I believe the fake screenshot I posted is possible on a real SNES, but it would glitch out the moment anybody tried to steer... I could try it, I suppose, but given what happened when I tried switching from Mode 1 to Mode 7 mid-scanline, I don't expect a happy result. And the power supply constraint means there's not much point in trying anyway.

If you need any info definitely check out CatadorDeLatas's F-zero Notes Document:
He's done a lot of work to map out pretty much the whole game. Tons of RAM and ROM addresses with explanations. I'd say right now CatadorDeLatas knows the inner workings of SNES F-Zero more than anyone else besides the original developers.

He's mostly active in this F-Zero thread on SMWCentral:
He's made patches that make the CPU cars explode if they fall off the track and even gave them health like the player has (!).

Very interesting stuff.

I don't expect to be working on F-Zero myself any time soon, as the shmup port I'm working on is almost stalled (as are all of my other hobbies) due to the requirements of real life. But I can throw together a mockup in Paint/GIMP pretty easily when the mood strikes me...

f-zero_2player.png [ 282.53 KiB | Viewed 1919 times ]

I'm assuming here that the SA-1 is powerful enough to decompress 4 KB of map data per frame (as discussed earlier in the thread) and render an 8-line-high strip of 2bpp software Mode 7 for each player right above the actual Mode 7 horizon, so as to let the players see further ahead and mitigate the visibility issues with the compressed perspective. This trick would also allow for faster speeds in the hypothetical SA-1-based F-Zero X prequel I like to daydream about - GP Legend features X-class speeds with SNES-style graphics, and I find it too fast for the draw distance...

Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 27 posts ]  Go to page Previous  1, 2

All times are UTC - 7 hours

Who is online

Users browsing this forum: melanokardios, Yahoo [Bot] and 7 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