6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Nov 23, 2024 12:54 pm

All times are UTC




Post new topic Reply to topic  [ 83 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6  Next
Author Message
PostPosted: Thu Jun 22, 2017 11:11 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8509
Location: Midwestern USA
Oneironaut wrote:
What I meant by Pseudo-Ops are fake instructions that some assemblers allow as legal instructions.

In other words, you mean synonyms for instructions that are part of the official assembly language. For example, I've often seen BLT used as a synonym for BCC in 6502 assembly language (BLT means branch if less than). I have to say such "fake" instructions are annoying, since one never knows for sure what they are supposed to do.

Like Garth, I tend to use a lot of macros, especially in code that sets up stack frames, in which a simple mistake can send the machine into the swamp. For example, I've been working on a display driver package that allows a program to do all sorts of stuff on the console screen, from changing attributes in a character string using embedded macros to drawing shapes such as graphic lines and rectangles. The underlying code tends to be complicated and the setup required to call some functions can be easily botched with a typo. Here's an example of where a macro saves the day, so to speak, in drawing a rectangle. The subroutine call requires a stack frame containing five parameters, which have to be in a certain order:

Code:
;   ————————————————————————————————————————————————————————————————————————
;   termrect: Draw Rectangle (macro)
;
;       termrect width,height,col,row,attr
;
;       col,row set the coordinates for the rectangle's top left corner.
;       Attributes (attr) are defined as follows:
;
;           00000000xxxx0xxx
;           ||||||||||||||||
;           |||||||||||||+++———> color
;           ||||||||||||+——————> reserved
;           |||||||||||+———————> 1: underline
;           ||||||||||+————————> 1: blink
;           |||||||||+—————————> 0: normal
;           |||||||||            1: reverse
;           ||||||||+——————————> 0: foreground
;           ||||||||             1: background
;           ++++++++———————————> reserved
;
;         Color bits are ignored in current version of the terminal manager.
;         The reserved bit should be set to 0.
;   ————————————————————————————————————————————————————————————————————————
;
termrect .macro .w,.h,.c,.r,.a
         pea .a
         pea .r
         pea .c
         pea .h
         pea .w
         jsr termrect
         .endm

Much easier to call that macro than to try to remember which parameter goes where. Also, the assembler will flag an error if the wrong number of parameters is passed with the macro call.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Last edited by BigDumbDinosaur on Fri Jun 23, 2017 1:37 am, edited 2 times in total.

Top
 Profile  
Reply with quote  
PostPosted: Thu Jun 22, 2017 11:30 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8545
Location: Southern California
BigDumbDinosaur wrote:
Oneironaut wrote:
What I meant by Pseudo-Ops are fake instructions that some assemblers allow as legal instructions.

In other words, you mean synonyms for instructions that are part of the official assembly language. For example, I've often seen BLT used as a synonym for BCC in 6502 assembly language (BLT means branch if less than).

Whether that's what he meant or not, I believe those are also called "aliases." They can be set up with macros if there's just one addressing mode, like BLT <label>, not something like BLT (ZP,X), BLT addr,Y, etc.. There are some commonly accepted ones like INA for INC A.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Thu Jun 22, 2017 11:38 pm 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
BigDumbDinosaur wrote:
The type of forward reference that arises when a zero (direct) page location is declared after the instruction that references it is avoidable, as you note. That is also the case for symbolic declarations of constants. However, most forward references are due to instructions whose operands refer to a yet-to-be-defined location in the source code. Such forward references cannot be avoided and can only be resolved by an assembler that makes a minimum of two passes through the source code, one pass to build and resolve the symbol table, and the other pass to generate the object code.

Yea. No.
Quote:
Assembler authors who claim that their assemblers build and resolve the symbol table, and generate the object code in one pass are being disingenuous, since an instruction early in the source file that has a location that is defined later in the source file cannot be assembled until that later definition has been entered into the symbol table.

Ok.

On the first pass, when you encounter a forward reference, you assemble the instruction anyway, leaving the argument with some filler (so, JMP AHEAD assembles to 4C FF FF, or something). You make a note in a list of where these forward references are in the final object code, along with their label reference. At the end, you iterate the list, dereference the labels, and back patch the real values in to the generated code. Obviously for branches, you perform the math and update the relative address. Only a single pass through the source is required, and there's no reason to retain it.

All Forth assemblers I've encountered are single pass assemblers.

But it should also be noted that single pass assemblers are rare.


Top
 Profile  
Reply with quote  
PostPosted: Thu Jun 22, 2017 11:52 pm 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
His point on Pseudo Ops are just what he was talking about. Instructions that look like operands, but in fact don't assemble to a single instruction, rather they assemble to something else. As Garth mentioned, macros can be a mechanism to create "pseudo-ops", but some architectures (and apparently ARM is one of them) has popular, or even official (I can't think of an example) "pseudo ops" for routine operations.

Of course the problem with the pseudo op is not that they offer convenience. Rather, it's that they hide detail. To a casual user and casual reader, they see a single "instruction" and they expect a specific impact on the resulting object code. But these "pseudo ops" are indistinguishable from normal operands and this, in fact, is by design, since the convenience is advocated. You "should" use these things. The designers intended for constructs like this to be used.

Even though macros can be used to implement pseudo ops, most macros stand out. They have more parameters, they have a longer name than most mnemonics, etc.

A good example of pseudo ops are those constructs used to shift the '816 from 8 <->16 Bit modes, since the actual instruction is pretty unintuitive. Also, sometimes you not only set the processor state, but the assembler state as well. You can see a novice on the platform curious why when you use the macro, you assemble one result, but if you use the actual opcode (and neglect to change the assembler state), you get another.

Just that annoying experience process raising its ugly head again as it resets your assumptions.


Top
 Profile  
Reply with quote  
PostPosted: Fri Jun 23, 2017 1:26 am 
Offline
User avatar

Joined: Mon May 25, 2015 2:25 pm
Posts: 690
Location: Gillies, Ontario, Canada
That's exactly what I was referring to.
I spent hours one night doing an ARM assembly VGA driver, counting cycles and checking code.
It wasn't until days later when I realized that many of the "official" ops were bogus.
Once I scratched them all of my list, my code worked as expected.
One lost cycle in a million?... yeah it makes a HUGE difference in the kind of things I like to do!

This discussion on assemblers has been a great source of info, so thanks!
Seems I always intended to do multi pass, and still can't fathom why you would try otherwise.

The fragment I have now, and where I was going was like this...

1) Start parsing source code at the beginning of the 4MB program space.
2) The assembly program counter is now set to 768, which is where all code must begin.
3) Convert simple instructions into their corresponding values and write them to the EXE segment.
4) If a ".ORG" is found, change the PC accordingly. Convert ".DAT" directly to values and store.
5) If a "LABEL:" is found, just ignore it.
6a) If a jump of some kind such as "JSR LABEL" is found, stop and re-parse the entire 4MB of code.
6b) Keep searching until the matching "LABEL:" is found, and then go back to 6a and insert the address.
7) For ".EQU", I intend to make it so they need to be declared before use, so it will be a find & parse type of thing.

So I guess with 40 jumps in code, my assembler will make 40 passes, or "searches".
The 6502 is able to fetch bytes at 8MHz from the Data Memory, so I can't see it being all that slow.
There is only really one instruction per line (128 bytes per line), so it should be efficient parsing as well.

If this doesn't work out, I will do some kind of address array to deal with labels and jumps.

Brad



whartung wrote:
His point on Pseudo Ops are just what he was talking about. Instructions that look like operands, but in fact don't assemble to a single instruction, rather they assemble to something else. As Garth mentioned, macros can be a mechanism to create "pseudo-ops", but some architectures (and apparently ARM is one of them) has popular, or even official (I can't think of an example) "pseudo ops" for routine operations.

Of course the problem with the pseudo op is not that they offer convenience. Rather, it's that they hide detail. To a casual user and casual reader, they see a single "instruction" and they expect a specific impact on the resulting object code. But these "pseudo ops" are indistinguishable from normal operands and this, in fact, is by design, since the convenience is advocated. You "should" use these things. The designers intended for constructs like this to be used.

Even though macros can be used to implement pseudo ops, most macros stand out. They have more parameters, they have a longer name than most mnemonics, etc.

A good example of pseudo ops are those constructs used to shift the '816 from 8 <->16 Bit modes, since the actual instruction is pretty unintuitive. Also, sometimes you not only set the processor state, but the assembler state as well. You can see a novice on the platform curious why when you use the macro, you assemble one result, but if you use the actual opcode (and neglect to change the assembler state), you get another.

Just that annoying experience process raising its ugly head again as it resets your assumptions.


Top
 Profile  
Reply with quote  
PostPosted: Fri Jun 23, 2017 1:56 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8509
Location: Midwestern USA
whartung wrote:
On the first pass, when you encounter a forward reference, you assemble the instruction anyway, leaving the argument with some filler (so, JMP AHEAD assembles to 4C FF FF, or something).

All multi-pass assemblers "dry assemble" instructions during pass one, which is how the assembler knows how much to advance the program counter per instruction (which, incidentally, is why forward references on zero page may cause a phase error). It is during the second pass that the actual object code is emitted, which is, of course, after the symbol table has been fully populated.

Quote:
You make a note in a list of where these forward references are in the final object code, along with their label reference. At the end, you iterate the list, dereference the labels, and back patch the real values in to the generated code. Obviously for branches, you perform the math and update the relative address. Only a single pass through the source is required, and there's no reason to retain it.

You just described a two-pass assembler.

I have been working with this stuff for 47 years and have never seen a symbolic assembler that could resolve forward references in a single pass. Every assembler I have used, and there have been quite a few of them, made multiple passes through the source code. The assembler that was available for the Basic 4 minicomputer system was a three-pass assembler that was able to correct some phase errors during the third pass. The Kowalski assembler, which has the luxury of building the (binary) object code entirely in core and could therefore patch instructions on the fly, requires two full passes through the source code to resolve forward references. My ancient UNIX 6502 cross-assembler (which I no longer use) makes two passes through the source as well.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Fri Jun 23, 2017 2:59 am 
Offline
User avatar

Joined: Mon May 25, 2015 2:25 pm
Posts: 690
Location: Gillies, Ontario, Canada
Ok, I am going to swat the rest of the bugs in my code (FPGA and 6502) and then dig deep into the assembler.

Here is my ToDo list that needs to be cleared before I move on...

1) Fix bug where EC-F1 does not always restart OS properly (you can see that bug in action in the video).
2) Add extended keys to PS2 decoder. It only does standard keys right now.
3) Add caps lock LED control to keyboard. Numlock /Scroll lock LEDs will show OS / EXE segment active.
4) Fix Bressenham line draw code. It has some errors at the end of the line.
5) Fix character set. Current 6x8 font was a rush job, and some lower case look like hell.
6) Test high speed bitmap blitter. This should run at 20MHz bandwidth.
7) Add in/out control so that bytes can be sent to the AVR to ask for SD card address locations.
8) Finish hardware cursor so that arrow keys move cursor and scroll lines at top and bottom of screen.
9) Finish testing 4 voice stereo sound system thoroughly. It seems to work so far.
10) Make special arrow + space key pseudo-joystick command so the 6502 sees it that way.
11) Add Box Collision test function to Math Command Set. Will test two rects for intersection.
12) Test frame and filled circle drawing functions.
13) Add "Serial Read" and "Serial Write" commands to send data to or from USB to Serial port.
14) Add internal logic for 3-8 decoder and solder on another 4 SRAMs to go from 2MB to the full 4MB.

The above list is about one day of work (two days including #14) if I can actually get a rainy day off.

I will keep thinking about my final assembler design as well.
Perhaps I just got lucky with the assembler I managed to get working so far, as it seemed fairly straight forward.
After reading all of the in-depth posts on the subject, I see that there is a lot more to it than what I thought.
Parse text, replace labels with numbers and execute... that's about all I am doing so far.

Brad


Top
 Profile  
Reply with quote  
PostPosted: Fri Jun 23, 2017 4:17 am 
Offline
User avatar

Joined: Mon May 25, 2015 2:25 pm
Posts: 690
Location: Gillies, Ontario, Canada
Ah, the nice thing about working with HDL and FPGAs... when you need a gate or chip, you just write one!

To connect 4 more SRAMs to make 8 x 512K, I needed a 4ns 3-8 multiplexer.
Imagine the cost on a gate that fast?

Instead, I just wrote one in a few lines of code...

Code:
assign SRAMCE = (MEMADR[21:19] == 0) ? 'b11111110 :
                (MEMADR[21:19] == 1) ? 'b11111101 :
                (MEMADR[21:19] == 2) ? 'b11111011 :
                (MEMADR[21:19] == 3) ? 'b11110111 :
                (MEMADR[21:19] == 4) ? 'b11101111 :
                (MEMADR[21:19] == 5) ? 'b11011111 :
                (MEMADR[21:19] == 6) ? 'b10111111 :
                'b01111111;


pretty simple, eh?

I connect address bits 0-18 directly to the SRAM address lines (all tied in parallel).
The "assign" then checks address bits 19-21, and does a 3-8 decode.
The decoded value is then send to the SRAMCE IO pins, which are the 8 SRAM CE lines.

Assigned statements like this happen as fast as the FPGA can propagate.
If I remember correctly, my Spartan-6 is about 4ns internally.

All of this cutting edge hardware just to serve a vintage processor coming out of retirement.
... 6502 deserves it!

Brad


Top
 Profile  
Reply with quote  
PostPosted: Fri Jun 23, 2017 7:25 am 
Offline

Joined: Thu Mar 03, 2011 5:56 pm
Posts: 284
BigDumbDinosaur wrote:
whartung wrote:
You make a note in a list of where these forward references are in the final object code, along with their label reference. At the end, you iterate the list, dereference the labels, and back patch the real values in to the generated code. Obviously for branches, you perform the math and update the relative address. Only a single pass through the source is required, and there's no reason to retain it.

You just described a two-pass assembler.


No, he didn't. He described an assembler that makes a single pass through the source code, then uses the symbol table along with a table of references to symbols to patch in the correct values. Nothing particularly exotic about this, but it does require an additional table.

Another example of a single-pass assembler is Henry Baker's "Comfy", although this is probably not something that you should expect to run on a 6502-based computer :)


Top
 Profile  
Reply with quote  
PostPosted: Fri Jun 23, 2017 7:37 am 
Offline

Joined: Sat Jun 04, 2016 10:22 pm
Posts: 483
Location: Australia
It does technically make another pass when it patches in the references. Depends on how you think about it, I suppose.


Top
 Profile  
Reply with quote  
PostPosted: Fri Jun 23, 2017 7:40 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8545
Location: Southern California
It seems like it would work just as well (or better) to just do a second pass and find and update all the forward references than to try to record them and go back and patch them. I wrote a two-pass 65c02 assembler with a symbol table in 1989 or 1990 as my first Forth project when I was totally green and inefficient at it. I don't think I ever put it into actual service.

Anyone up for a separate topic on one- and two-pass assemblers? I'd kind of like to say more, but if it's here, I want to make sure it's ok with Brad since this is his topic. What's your preference, Brad? You did bring up assemblers. OTOH, is there really enough additional to discuss about it at this point to warrant starting another topic?

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Fri Jun 23, 2017 7:56 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10986
Location: England
I'd say just do it - topics are cheap, and it's better to start a topic than to discuss starting a topic! If it dies on the vine, so be it. But in all likelihood there will be room for interesting discussion.


Top
 Profile  
Reply with quote  
PostPosted: Fri Jun 23, 2017 2:12 pm 
Offline
User avatar

Joined: Mon May 25, 2015 2:25 pm
Posts: 690
Location: Gillies, Ontario, Canada
Yeah, probably a good plan... I wasn't trying to open a can of worms!
Single pass, double pass, all access pass... it does not matter to me what the conventions are.
... I just hit the ground running and build things. I learn why they worked later!

Take Verilog and FPGAs for instance...
When I first looked at starting, the info out there on the net almost scared me off of trying.
I decided to just dive in and do things my own way, and now I can design in HDL at a very rapid rate.
Sure, I do things in such a way that would make most "experts" scoff, but hey... I get **** done!
I actually have one design out there in production even... a CNC video interface and motor control.
In the end I found that doing FPGA design is really just as easy as writing C for any micro-controller.
It's not a web friendly world for "hackers", like myself though. This is one exception I know of...

http://www.fpga4fun.com/

Doh... I am wandering off topic now.
I want to keep it to the A65 project, and mostly about the 6502.

So at this point, I am going to continue with my assembler and not worry about what box it fits into.
As mentioned, I plan on doing an entire "pass" every-time I encounter a label, so this is probably not the "norm".
I will just call it "brute force pass" and move on to writing code!

It was interesting to read about the different types of assemblers though, so thanks for the info.
If I fail at my task, then I may take the time to look deeper into some of these links I have bookmarked.

Ok, on to the bug swatting and command testing.
I want everything inside the FPGA working perfectly before I continue with the assembler.

Cheer,
Brad

BigEd wrote:
I'd say just do it - topics are cheap, and it's better to start a topic than to discuss starting a topic! If it dies on the vine, so be it. But in all likelihood there will be room for interesting discussion.


Top
 Profile  
Reply with quote  
PostPosted: Fri Jun 23, 2017 6:58 pm 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
BigDumbDinosaur wrote:
Such forward references cannot be avoided and can only be resolved by an assembler that makes a minimum of two passes through the source code, one pass to build and resolve the symbol table, and the other pass to generate the object code.

Emphasis mine
BigDumbDinosaur wrote:
You just described a two-pass assembler.

Or, you know, I didn't. Being as the source was only seen once.
BigDumbDinosaur wrote:
Every assembler I have used

What about the ones you haven't used?

Many assemblers run through the source once, create a derivative version (macros expanded, labels identified, in an internal format rather than just text), then "pass through" that derivative data to help create the object code. Single pass assemblers create object code on their first pass, and then patch the image at the end. The object code may be incomplete before the patching, but it's not a secondary representation -- mostly since it's at this point an opaque blob to the assembler (it's an assembler, not a disassembler).

This is a Forth assembler and, I can assure you, it only sees the source code once. The conditionals leverage the stack to keep track of back patching addresses for resolution at the end of the conditional (the REPEAT word in this case cleans everything up).

(I've expanded this code to look more like "normal" assembly. Typically for assembly doesn't look like this in practice.)
Code:
CODE TRAVERSE (S addr direction -- addr' )
  CX POP
  BX POP
  CX BX ADD
  ES PUSH
  YSEG #) ES MOV
  BEGIN
    ES: 0 [BX] AL MOV
    128 # AL AND
  0= WHILE
    CX BX ADD
  REPEAT
  ES POP
  BX PUSH
  NEXT
END-CODE

This is a simple example (in traditional Forth style), since there's no control structures, the generate binary is just left in place and used. (1PUSH is a macro, or a "pseudo op" in this case.)
Code:
CODE PR-STAT (S -- f )   DX DX XOR   2 # AH MOV   23 INT
   32768 # AX AND   1PUSH   END-CODE

Here's an example of the system dealing with the nature of working with a single pass assembler:
Code:
LABEL DONE   ASSEMBLER
  CX PUSH   NEXT

CODE  SKIP   (S addr len char -- addr' len' )
  AX POP   CX POP   DONE JCXZ   DI POP   DS DX MOV
  ES BX MOV   DX ES MOV
  REPZ BYTE SCAS   0<> IF   CX INC   DI DEC   THEN
  DI PUSH   CX PUSH   BX ES MOV   NEXT   END-CODE

CODE  SCAN   (S addr len char -- addr' len' )
  AX POP   CX POP   DONE JCXZ   DI POP
  DS DX MOV   ES PUSH  DX ES MOV  CX BX MOV
  REP BYTE SCAS    0=  IF   CX INC   DI DEC   THEN
  ES POP  DI PUSH   CX PUSH   NEXT   END-CODE

Note that DONE is defined first. In normal code, we'd have DONE a the bottom of the listing, and have the code jump "down" to it. Here, it jumps back up. NEXT is Forths mechanism of jumping to the next word.

It can do this because Forth is not a "top down" program that starts at PC and goes to PC+1, rather it's a collection of words that get jumped to as necessary. So, there is no concern of the program just "running in to" the DONE block at the top like in a normal assembly program, and no reason to just "jump" around it. This DONE block lives in a code space "no mans land" between defined words. SKIP and SCAN are both entered in to the Forth dictionary as normal words.

Single pass assemblers are not the norm, they're for special cases where "some" assembly is better than "none" in constrained environments. Their most useful purpose today is simply as an example of why we have two pass assemblers.


Top
 Profile  
Reply with quote  
PostPosted: Sun Jun 25, 2017 5:23 pm 
Offline
User avatar

Joined: Mon May 25, 2015 2:25 pm
Posts: 690
Location: Gillies, Ontario, Canada
Optimizing Verilog code today... boring!

So far, I have gone from 90% utilization to 78%, so this is good.
I was getting worried when at 90% full, since I have not done the File System Commands yet.

Unlike optimizing assembly, which is a fun a quick process, HDL is slow and extremely painful.
You are really just fighting the compiler here, and each try is a 4 minute wait!

In assembly, you would see something like this...

Code:
LDA #0
STA 62000


And simply change it to this and be done...

Code:
STZ 62000


But in Verilog, you are sometimes just messing around with syntax to make HUGE gains.
In another program, I used this type of statement...

Code:
if (MODE == 0) begin
if (TEST == 123) RESULT <= 321;
if (TEST == 456) RESULT <= 654;
if (TEST == 789) RESULT <= 987;
end else begin
RESULT <= 0;
end


Soon, my compile (synthesize) went from 4 minutes to 30 minutes, and it wasn't even a long bit of code.
I simply "re-worded" my code to get the same end result, and was back to a 4 minute compile...

Code:
if (MODE == 0 & TEST == 123) RESULT <= 321;
if (MODE == 0 & TEST == 456) RESULT <= 654;
if (MODE == 0 & TEST == 789) RESULT <= 987;
if (MODE != 0) RESULT <= 0;


Although the code above is a simplified version of what I had, the semantics were exactly the same.
Both codes compile to exactly the same, but the monkey in the black box had to chew on the first for an extra 20 minutes!

Verilog and VHDL are strange beasts that way indeed. You can change one little thing in code, and it will effect something else non related.
Typically, in a day of Verilog programming, I would say that 90% of my time is either waiting for compile, or fighting the compiler monkey.
It really is a simply language to learn and use, though.

Oh, and I fixed my "OS Return" glitch this morning as well.
Seems that my Zero Page reload counter stopped at 254 rather than 255, and that is where the OS stores it's "Power-on or Re-run" flag.
When the OS runs for the first time, it writes 255 at Zero Page location 255.
This way, it knows that it is returning from a bank swap, and can keep its IDE settings.

I am now going to redo my PS2 decoder, as it is a real hackjob.
Looking for a much nicer way to go from scan codes to ascii.
I may just forget ascii, since I don't plan in linking to the modern world anyhow.
My OS / IDE can use whatever it likes for character codes since I am writing the rules here.

Ok, gotta go... Xilinx ISE just finsihed another 6 minute synthesize, and I changed a single variable!
Time to load and test....

Radical Brad


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 83 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6  Next

All times are UTC


Who is online

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