It is currently Mon Oct 15, 2018 10:36 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 42 posts ]  Go to page Previous  1, 2, 3  Next
Author Message
 Post subject: Re: Learning MMC1
PostPosted: Wed Sep 19, 2018 4:39 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3629
Location: Mountain View, CA
tepples wrote:
Would the following be correct and concise?
Code:
$E000-FFFF:  [.... ...L]
    L = Serial load register (LSB first)

Internal register after fifth write to $E000-FFFF:
             [W PPPP]
    W = WRAM Disable (0=enabled, 1=disabled)
    P = PRG Reg

No! Stop! Halt! Cease and desist! That is equally confusing, just in a different way. But this doesn't matter!

Foremost, and THE MOST IMPORTANT OVERLOOKED FACT: we don't know what documentation the OP was looking at when he made this thread because he didn't reference it. But I can tell from the syntax/formatting that it's probably Disch's mapper document for MMC1. Let's see if I'm right: http://www.romhacking.net/documents/362/ -- file 001.txt within the zip. Yep, I'm right.

Two: https://wiki.nesdev.com/w/index.php/MMC1 (our iNES_Mapper_001 page redirects there as well) contains its own version/description of these registers, which is different (read: different terminology compared to Disch's docs), but that's OK because it's generally consistent. So is Disch's doc, actually, because his 001.txt doc has a section titled "Notes" that explains the MMC1's behaviour (keep reading). So, I believe the OP did not read the doc, but instead skimmed it. Bzzzt! :-)

He should probably read the Wiki page, as user unregistered said. (That's probably the best post in this thread so far!)

Three: said Wiki page contains a section called "Registers" which has a very well-written 2-paragraph description of the behaviour at the very top. The description uses both words and code, and is excellent at describing how the MMC1 behaves (that its registers are single-bit due to simple use of a shift register). This section does not need changing: it reads just fine and can be comprehended by programmers and hardware people alike, including those who are new. It's a great section. The subsequent register descriptions are indicators make a lot more sense once reading aforementioned paragraphs. If those paragraphs still don't make sense, Disch's docs (section "Notes") actually goes through things step-by-step in a more hand-holding way for programmers.

In summary: there is nothing to change, but instead just point the user to some documentation that's more clear in this particular case: the Wiki page. Disch's docs are still relevant/applicable/legitimate for the most part, but they require the person to read them. The Wiki pages are the same way: you have to read.


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Wed Sep 19, 2018 5:01 pm 
Offline
User avatar

Joined: Wed Sep 05, 2018 11:13 am
Posts: 69
Location: Colorado
koitsu wrote:
tepples wrote:
Three: said Wiki page contains a section called "Registers" which has a very well-written 2-paragraph description of the behaviour at the very top. The description uses both words and code, and is excellent at describing how the MMC1 behaves (that its registers are single-bit due to simple use of a shift register). This section does not need changing: it reads just fine and can be comprehended by programmers and hardware people alike, including those who are new. It's a great section. The subsequent register descriptions are indicators make a lot more sense once reading aforementioned paragraphs. If those paragraphs still don't make sense, Disch's docs (section "Notes") actually goes through things step-by-step in a more hand-holding way for programmers.


Ok, I may just be slow, but I'm definitely missing something. I've read the wiki page and I've read Disch's docs, but what I don't get is what happens when you send your 5 bits to any of the given registers. I get that some sort of bank switching occurs, and it's my understanding that after you do this, there is a memory address A that now points to some memory address that is on the cartridge instead of the RAM that is on the NES. What I don't understand is how you know what address A is, or how you know where that is now pointing.

Another thing I don't understand is pages. So there are memory pages somehow associated with these bank switches, but I don't know how when you are writing your asm code you let the assembler know what these pages are or how they would get loaded with the mapper and to where.

I'm coming at this as a software guy and I've just not had to deal with anything like this before, so there feels to me like there are large gaps in the explanation of how this works, but as I said earlier, it may be that I'm just slow.

Thanks for your help

_________________
A few of my web games
https://www.embed.com
Or if you're bored at work
https://www.classicsolitaire.com


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Wed Sep 19, 2018 5:48 pm 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 7648
Location: Seattle
By default, the NES's CPU can only see 32 KiB of PRG on the cartridge, and the NES's PPU can only see 8 KiB of CHR on the cartridge. (exceptions exist, ignore them for now)

The simplest kind of bankswitching is like what CNROM does: it allows any one of many different 8 KiB banks of CHR to be seen by the PPU at once.

The value that's written to the register chooses which 8 KiB bank is seen. This is just a magic number, it has no particular significance.

A wide variety of other simple mappers apply the same concept to the CPU, or both at once.

UNROM is a similar idea, but divides the CPU's 32 KiB of PRG into two "windows", one of which can be controlled by software, the other is always the last bank.

As to how one know what's where? Because the programmer put it there. The question of how to allocate code and data to banks is largely unrelated to how bankswitching works, and I'm not clear one could even begin to grapple with understanding how to partition things until after you understand how the partitioning works at all.

So ... do you know that your project won't fit in plain NROM? Writing a game at all isn't exactly easy, and it'll be a lot easier to not worry about this extra hardware until after you feel you have a handle on everything else.


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Wed Sep 19, 2018 6:22 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10892
Location: Rio de Janeiro - Brazil
tepples wrote:
Code:
$E000-FFFF:  [.... ...L]
    L = Serial load register (LSB first)

Internal register after fifth write to $E000-FFFF:
             [W PPPP]
    W = WRAM Disable (0=enabled, 1=disabled)
    P = PRG Reg

FWIW, I think this is pretty clear.


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Wed Sep 19, 2018 6:27 pm 
Offline
User avatar

Joined: Wed Sep 05, 2018 11:13 am
Posts: 69
Location: Colorado
lidnariq wrote:
So ... do you know that your project won't fit in plain NROM? Writing a game at all isn't exactly easy, and it'll be a lot easier to not worry about this extra hardware until after you feel you have a handle on everything else.


Writing something really basic was pretty easy. My problem is I'm not sure how to make it scale. As I wrote earlier in this thread, I started out writing something where I just had a little man running around shooting little bullets.

My goal is to write something more interesting, so, I decided to grab some code off of github and see what they do. The code I grabbed looks like Metroid. They have a "Original" version and a MMC3 version. The MMC3 version wasn't working so I compiled and ran through the "Original" version, which looks like it's using MMC1. All I'm trying to do is learn more about NES dev. I don't have a specific game in mind at the moment, but the code I have is MMC1, so unless I can find a game example somewhere that is something else, that's what I'm going through.

I'm not sure I have a clear understanding of how partitioning works. I'm just having a hard time going from the simple Nerdy Nights understanding of how to write a NES game to being able to write something more interesting than a single screen arcade game. I would like to be able to have scrolling in 2 directions (eventually), I'm not sure that can be done on MMC1 except in maybe a Zeldaish sort of way.

Anyway, I'm used to high level programming languages, and going from a high level language to a simple single screen arcade game seems not too bad. I'm just having trouble moving from that to something more complicated. Trying to understand someone elses code seemed like a good way to do that... which leads me back to trying to understand how MMC1 works.

Thanks

_________________
A few of my web games
https://www.embed.com
Or if you're bored at work
https://www.classicsolitaire.com


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Wed Sep 19, 2018 6:32 pm 
Offline
User avatar

Joined: Wed Sep 05, 2018 11:13 am
Posts: 69
Location: Colorado
lidnariq wrote:
I'm not clear one could even begin to grapple with understanding how to partition things until after you understand how the partitioning works at all.


I just searched nesdev for partitioning and nothing came up.

I also googled for nes assembly partition and couldn't find anything useful.

If you have a link to any good information on the topic I would appreciate it.

Thanks

_________________
A few of my web games
https://www.embed.com
Or if you're bored at work
https://www.classicsolitaire.com


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Wed Sep 19, 2018 6:59 pm 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 7648
Location: Seattle
I'm just using "partitioning" to describe the process of allocating data, graphics, and code to different banks.


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Wed Sep 19, 2018 7:47 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3629
Location: Mountain View, CA
Well, I did a huge gigantic write-up on all of this, talking about UNROM at first, but then decided to back it out because explaining bus conflicts is confusing as hell to newcomers. Then I changed my write-up to talk about MMC1... except that as I begun to look at the Wiki, both the main MMC1 page and the Programming MMC1 page, there's information there that feels shoddy, and in some cases, half-ass. The couple paragraphs in the "Registers" section are good, but for overall comprehension these pages are crazy. I've dealt with this mapper on a general level (i.e. reverse-engineered games already using it), but reading this documentation makes even my head spin. I don't even know where to start with what's awful about them (we're back to essentially this thread's overall gist, but for MMC1 instead of PPU). I'm starting to notice a common trend here where the pages that I think suffer from this problem are often heavily edited by people who work with hardware or are hardware-savvy. Imagine that. But you can't deny that a page called "Programming MMC1" should actually be intended for programmers and explain things in a common "this is how you use it" way, and not cheap out with phrases like "do this after you've done some other stuff to the mapper first :) Which we don't tell you :) Look at the MMC1 page instead :) Have fun :)". Hahaha :P

Thus, I've emptied my post/reply, because I do not want to confuse the OP.

Edit: clarification.


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Wed Sep 19, 2018 7:55 pm 
Offline
User avatar

Joined: Wed Sep 05, 2018 11:13 am
Posts: 69
Location: Colorado
lidnariq wrote:
I'm just using "partitioning" to describe the process of allocating data, graphics, and code to different banks.



Gotcha. I've noticed that some assemblers use .org to do that and some use .segment with a keyword that in the case of ca65 is defined in a .cfg file.

This came out of rainwarrior's ca65 template:

Code:
MEMORY {
    ZP:     start = $00,    size = $0100, type = rw, file = "";
    OAM:    start = $0200,  size = $0100, type = rw, file = "";
    RAM:    start = $0300,  size = $0500, type = rw, file = "";
    HDR:    start = $0000,  size = $0010, type = ro, file = %O, fill = yes, fillval = $00;
    PRG:    start = $8000,  size = $8000, type = ro, file = %O, fill = yes, fillval = $00;
    CHR:    start = $0000,  size = $2000, type = ro, file = %O, fill = yes, fillval = $00;
}

SEGMENTS {
    ZEROPAGE: load = ZP,  type = zp;
    OAM:      load = OAM, type = bss, align = $100;
    BSS:      load = RAM, type = bss;
    HEADER:   load = HDR, type = ro;
    CODE:     load = PRG, type = ro,  start = $8000;
    RODATA:   load = PRG, type = ro;
    VECTORS:  load = PRG, type = ro,  start = $FFFA;
    TILES:    load = CHR, type = ro;
}


So it looks like the ZP and the CHR sections overlap. So in the code I was messing around with, I had

Code:
.segment "TILES"
.incbin "combined.chr"


What I don't understand is how the segment "TILES" relates to the Memory CHR. If when I call .segment and pass it "TILES" where in memory does it drop that .chr file?

I know these are really basic questions, I have a lot of gaps in my understanding coming from a background programming in higher level languages.

Thanks

_________________
A few of my web games
https://www.embed.com
Or if you're bored at work
https://www.classicsolitaire.com


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Wed Sep 19, 2018 8:15 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6875
Location: Canada
battagline wrote:
So it looks like the ZP and the CHR sections overlap. ... What I don't understand is how the segment "TILES" relates to the Memory CHR. If when I call .segment and pass it "TILES" where in memory does it drop that .chr file?

The MEMORY statements in the linker config are just arbitrary spaces to put data. If file = %O then the contents of that space will go to the output file. If file = "" the contents won't go anywhere. We use the first type of MEMORY region to lay out the .NES ROM file (file format). We use the second type of MEMORY region to organize static RAM allocations.

So, ZP, OAM, RAM are all RAM.

HDR, PRG and CHR are all sections of the iNES file (see the file format link above).

Inside an NES cartridge you will find two ROM chips. One is called CHR, one is called PRG. In the iNES file, these are different sections of the file. One belongs to the PPU (graphics unit), and one belongs to the CPU. The two of these occupy completely different memory spaces (PPU vs. CPU).

The HDR as another block of 16 bytes that goes at the beginning of the file, so it's memory space is also unrelated and separate.

Now, all of the RAM stuff is actually in CPU space. The PRG ROM only starts at $8000, and everything below that is various things, uncluding the RAM. The CHR and HDR are both in their own space, so they aren't actually overlapping here.


The SEGMENT sections are used to fill up MEMORY spaces. TILES has 'load = CHR' which puts it in the CHR MEMORY block.


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Wed Sep 19, 2018 8:26 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3629
Location: Mountain View, CA
battagline wrote:
What I don't understand is how the segment "TILES" relates to the Memory CHR. If when I call .segment and pass it "TILES" where in memory does it drop that .chr file?

Yeah, this isn't inherently obvious. In fact, I would argue it's more obvious when you see how this is done using something like a batch file to "create" a NES ROM file, e.g. copy /b prg00.bin+prg01.bin+prg02.bin+prg03.bin+tiles.chr > mygame.nes. I've always maintained the learning curve for ca65/ld65 is high as well.

What you need to focus on here is the MEMORY section, specifically HDR, PRG, CHR. Note how those actually have a file associated with them, as well as fill = yes? These associated sections end up in the resulting .nes ROM file. More on that at the end of this post.

Once you understand the NES file format and it's 16-byte header (that's what HDR contains) it might start to make more sense to you: https://wiki.nesdev.com/w/index.php/INES

- 16-byte header
- Multiple 16KByte PRG-ROM banks/pages (depends on header and mapper)
- Multiple 8KByte CHR-ROM banks/pages (depends on header and mapper)

The header contains the number of 16KB PRG banks, and the number of 8KB CHR banks, that make up the rest of the file. It also contains the mapper number. From there it's simple math to know how big the rest of the file will be, and what to load where into CPU memory space as well as PPU memory space if applicable. That leads me to this:

For games which lack a mapper (ex. mapper 0 or NROM), there is a single 8KByte CHR-ROM bank (i.e. the header will just contain "1" for CHR count). This is literally the equivalent of an 8KByte mask ROM (chip) on the cartridge which contains graphics.

Those graphics are part of the PPU memory space, specifically $0000-1FFF, otherwise known as the pattern table. I will call that 8KByte region "pattern table" below.

For games which use CHR-ROM but with a mapper that lets you swap CHR banks, it's identical to the above, except instead of an 8KByte mask ROM for CHR, you might have, say, a 64KByte mask ROM -- so that's eight (8) 8KByte CHR banks/pages. You can swap those in/out of the pattern table in real-time through the mapper; it's instantaneous.

For games which use CHR-RAM (note: RAM, not ROM!), the cartridge contains a single 8KByte RAM chip, correlating to the pattern table. Thus, the NES header will contain "0" for CHR bank count. In this situation, it's up to your 6502 code to copy data (the tiles) from elsewhere in PRG-ROM into the pattern table via MMIO registers $2006/2007. Games of this sort tend to have larger PRG-ROM (ex. 128KBytes) because the graphics/data are contained within there.

You can read about the pros and cons of both CHR-ROM and CHR-RAM here: https://wiki.nesdev.com/w/index.php/CHR_ROM_vs._CHR_RAM

The final piece of the puzzle:

Segment TILES actually refers to the MEMORY area/thing called CHR. It happens to contain 8KBytes of graphics loaded from the file called combined.chr via .incbin as you noticed. That 8KByte file/data ends up in the CHR bank/page section of the resulting NES file, thus ending up in the pattern table in the NES.

Edit: sniped by rainwarrior. :-)


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Wed Sep 19, 2018 8:32 pm 
Offline
User avatar

Joined: Wed Sep 05, 2018 11:13 am
Posts: 69
Location: Colorado
rainwarrior wrote:
Inside an NES cartridge you will find two ROM chips. One is called CHR, one is called PRG. In the iNES file, these are different sections of the file. One belongs to the PPU (graphics unit), and one belongs to the CPU. The two of these occupy completely different memory spaces (PPU vs. CPU).

The HDR as another block of 16 bytes that goes at the beginning of the file, so it's memory space is also unrelated and separate.


Ok, I think maybe I'm starting to get it... maybe

There are really 3 distinct parts of the iNes file.

1.) Header Information
2.) CPU Memory
3.) PPU Memory

Header information is just there to help the emulator figure out what to do. The data that would have been in the CHR ROM file gets loaded into the PPU Memory, and the data that would have been on the PRG ROM file gets loaded into the CPU Memory.

Is that a reasonably close explanation?

It looks like in reality it may be more complicated that that, but I just want to make sure I understand the basics.

Thanks

_________________
A few of my web games
https://www.embed.com
Or if you're bored at work
https://www.classicsolitaire.com


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Wed Sep 19, 2018 11:38 pm 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 7648
Location: Seattle
Yeah, that's a reasonable proxy for what happens in the actual NES.

As you said, it's glossing over a lot of edge cases and is a little simplified relative to what the hardware actually does, but it's a reasonable proxy for understanding.

You might find the recent open-source platformer Nova The Squirrel a more useful reference than the reverse-engineered Metroid.


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Thu Sep 20, 2018 12:13 am 
Offline
User avatar

Joined: Tue Jun 24, 2008 8:38 pm
Posts: 1994
Location: Fukuoka, Japan
I know this topic is about MMC1 and I did read most of it but if you are just starting with nesdev, if possible, it is better to avoid mapper since it won't bring much to your learning process. But you can still ask questions about it while learning more about the nes in general in a mapper-less environment.

If I remember from the topic "mesen label files", you have a background in programming and did make games, which means you probably touched C to some degree during those years. In that case, since you are testing more and more with cc65, you could do a lot of experiment in C, in a nrom (no mapper) and use neslib as a base for your testing.

With that foundation, you should be able to first test how the nes work, create a dbg file which can be imported in mesen and put break point to check how your code react and why. That would helps alot. Then when you start to get an hang on how to define segments, you can write some asm code that can be accessed from you C code and do more advanced nes related action (ppu, audio etc). Once you're confortable, mm1/mmc3 etc will be quite easy at that stage.

But, it's up to you, you decide how you want to learn and what interest you but for now mappers are maybe not the most important thing to learn since it's not part of the nes anyway: it's on the cartridge directly ;)

If you need help in C and how to use, many people knows how to create basic project. I have a make file that makes it simple too.


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Thu Sep 20, 2018 12:48 am 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3629
Location: Mountain View, CA
Yup, you got it. I kept a copy of what I previously wrote around, so it might be a good time to expand a bit on that:

The term "page" on the NES can refer to one of many different things. You'll see this term used a lot, and that adds to the confusion -- this can't really be avoided, sadly:

- The 6502 CPU: a "page" of memory. This refers to 256-byte sections of memory space. For example, $0000 to $00FF would be page 0 (also known as "zero page"); $0100 to $01FF would be "page 1" (also known as the stack area), $0200 to $02FF would be "page 2", etc.. These are numbered 0 to N.

- About PRG and CHR: a "page" of ROM. Other terms used for this are "bank" (common), "section" (uncommon), or "unit" (uncommon; and shame on whoever invented this one). Use whichever term makes more sense to you, but to relieve confusion prefix it with the word PRG or CHR (ex. "PRG bank 7"). The size of a page depends on if it's PRG or CHR, and depends on what the mapper is that's used. These are numbered 0 to N.

- About the .NES file format/header bytes 4 and 5: again, referring to the number of PRG or CHR pages/banks. Here's the rub: the sizes are static -- 16KB PRG banks, and 8KByte CHR banks -- regardless of mapper! This is because the file format was invented before some mappers were discovered that used smaller than 16KB PRG or 8KB CHR pages. These are numbered 1 to N, but the maximum depends on the mapper of course. I also want to add here that your PRG and CHR counts need to be in proper n^10 quantities; ex. PRG of 16KB, 32KB, 64KB, 128KB, 256KB... CHR of 8KB, 16KB, 32KB, etc..

The reason it's new to you is because you've probably worked on systems which have always provided large amounts of memory / addressing space, and always linearly. The 6502 is substantially more limited in this regard, made during a time where things like 16KBytes of memory was considered very large.

Here is a CPU memory map. It's 64KB ($0000 to $FFFF), but as you can see, sections of it are cut out/dedicated to certain things: $0000 to $07FF is for RAM (so pages 0 through 7), and so on. Ignore the silly pedantic nonsense you see about how "cartridge space" is between $4020 and $FFFF -- this will just confuse you right now, so let's be pragmatic, because you're a programmer:

PRG exists from $8000 to $FFFF in CPU memory space. That's a 32KB section of memory that correlates directly with what's on the cartridge. The PRG you know from the .NES file format? Your code? Et cetera? It all resides there (barring situations where, say, you might want to copy some code into RAM yourself -- separate subject, but entirely doable). Addresses $FFFA to $FFFF are special/unique/important, as these contain the 6502's vectors (RESET, NMI, and IRQ/BRK) -- they contain the addresses of where to start when the machine is powered on/reset, or when NMI happens (e.g. every VBlank), or when a hardware IRQ (or BRK instruction) occurs. Those addresses are unique/specific to the 6502 CPU.

CHR exists in PPU memory space, specifically PPU address $0000 to $1FFF, otherwise known as the pattern table. These contain your graphics (most of them anyway; I'm intentionally ignoring covering things like nametables, attribute tables, sprites, etc.. Stay focused!)

So, an NROM (mapper 0) game that's 40KB in size would be:

- 2x 16KB of PRG (code/data) -- one 32KB mask ROM chip
- 1x 8KB of CHR-ROM (graphics) -- one 8KB mask ROM chip

Make sense so far?

So what happens if you have a game that needs more than 32KB of PRG, or more than 8KB of CHR? You need a mapper that can let you "change" what the CPU memory space (for PRG) or PPU memory space (for CHR) contains. And you need to be able to switch things around in real-time using 6502 instructions.

Still following? Great. So let's imagine a larger game, maybe one that's 136KB -- lots of code/data

- 8x 16KB (128KB) of PRG (code/data) -- one 128KB mask ROM chip
- 1x 8KB of CHR-RAM -- one 8KB RAM chip

Ignore the fact that there's RAM instead of ROM for CHR like earlier. We're focused on code right now, not graphics.

A mapper with PRG swapping allows portions of, or all of, that $8000-FFFF region to "point" to different portions on the actual mask ROM chip. "Portions" commonly means either 16KB or 32KB portions, but it varies per mapper (there are some that use 8KB PRG-ROM banks, like the MMC3).

Likewise, for CHR-ROM swapping, the same concept applies but usually in 4KB or 8KB pages. But some mappers let you swap portions as granular as 1KB, and others let you select the page size to swap in real-time (MMC3 is, again, a good example).

I'll follow up to this post with another one specifically talking about MMC1.


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

All times are UTC - 7 hours


Who is online

Users browsing this forum: Gilbert, Google Adsense [Bot] and 5 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