6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Mon May 13, 2024 12:02 am

All times are UTC




Post new topic Reply to topic  [ 338 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6, 7 ... 23  Next
Author Message
PostPosted: Wed Mar 20, 2019 8:48 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 858
I'm still working on getting multitasking and vectored I/O playing nice together. One of my test tasks is a clock that displays the time in the top right corner of the screen and I don't want the time to be printed when I am listing something or logging a session. I had thought about user deferred words but really didn't like the idea much (the user area for each task would increase.)
I'm thinking of trying something like this:
Code:
' NOOP VALUE CIO  // CURRENT I/O

SCR# 32
// CONSOLE PRINTER
: CONSOLE  ( -- )
   [ LATEST NAME> ] LITERAL IS CIO
   #LP CLOSE  CLRCHN
   ['] (EMIT) IS EMIT
   ['] (TYPE) IS TYPE
   ['] (QTYPE) IS QTYPE
   ['] (EXPECT) IS EXPECT ;
: PRINTER  ( -- )
   [ LATEST NAME> ] LITERAL IS CIO
   #LP CLOSE
   0 0  #LP DUP #LP2 (OPEN) IOERR
   ['] (PEMIT) IS EMIT
   ['] (PTYPE) IS TYPE
   ['] (PQTYPE) IS QTYPE
   ['] (EXPECT) IS EXPECT ;

SCR# 33
// LOGGER
: LOGGER  ( -- )
   PRINTER
   [ LATEST NAME> ] LITERAL IS CIO
   ['] (LEMIT) IS EMIT
   ['] (LTYPE) IS TYPE
   ['] (LQTYPE) IS QTYPE
   ['] (LEXPECT) IS EXPECT ;
CONSOLE

CIO is a value that holds the CFA of the current I/O redirection word. Each of the three I/O redirection words in Fleet Forth stores the address of it's own CFA in CIO. If a background task has to temporarily redirect I/O, it could do something like this:
Code:
... SINGLE CIO
   <REDIRECT I/O AND DO SOMETHING>
   EXECUTE  MULTI PAUSE ...

If a background task is setup for running one shot routines as needed and is normally asleep until activated, it could run a word with something like this:
Code:
... SINGLE CIO
   <REDIRECT I/O AND DO SOMETHING>
   EXECUTE  MULTI STOP ;


Top
 Profile  
Reply with quote  
PostPosted: Wed Mar 20, 2019 10:21 pm 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
You're not content with some kind of semaphore system protecting the resource? (Printer, screen, etc.)


Top
 Profile  
Reply with quote  
PostPosted: Thu Mar 21, 2019 10:06 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 858
whartung wrote:
You're not content with some kind of semaphore system protecting the resource? (Printer, screen, etc.)

I haven't added semaphores to the multitasker yet, but my understanding of their use is if you have more than one task ( or computer) and a single shared resource ( such as a printer), each task gets exclusive use until it finishes on a first access, first serve basis. In my system, the programmer ( the main or foreground task) has full use of the screen and printer. TYPE and EMIT use the Commodore 64 kernel routines for output. Here are the I/O redirection words as they were without multitasker support.
Code:
SCR# 2E
// OUTPUT REDIRECTION
HEX
// PRINTER DEVICE NUMBER
4 VALUE #LP
// PRINTER SECONDARY ADDRESS
0 VALUE #LP2
: (PEMIT)  ( C -- )
   #LP (CHKOUT) IOERR
   (EMIT) CLRCHN ;
: (PTYPE)  ( ADR U -- )
   #LP (CHKOUT) IOERR  (TYPE) ;

SCR# 2F
// <PQTYPE>
HEX
: <PQTYPE>  ( ADR CNT -- )
   #LP (CHKOUT) IOERR
   >ASSEM
   2 # LDA,  SETUP JSR,
   BEGIN,
      BEGIN,
         N CPY,
         0= IF,
            N 1+ LDA,
            0= IF,
               ' CLRCHN @ JMP,
            THEN,
            N 1+ DEC,
         THEN,

SCR# 30
// <PQTYPE> (PQTYPE)
         N 2+ )Y LDA,
         BL # CMP, CS IF,
            7B # CMP, CS IF,
               0C1 # CMP, CS IF,
               0DB # CMP, CS ELIF,
         CS-ROT THEN,
            5F # LDA,
            THEN,
         THEN,
         ' (EMIT) @ 8 + JSR,  INY,
      0= UNTIL,
      N 3 + INC,
   AGAIN,  END-CODE
: (PQTYPE)  ( ADR CNT -- )
   <PQTYPE> PAUSE ;

SCR# 31
// LOGGER REDIRECTION
HEX
: (LEMIT)  ( B -- )
   DUP DEMIT  (PEMIT) ;
: (LTYPE)  ( ADR CNT -- )
   2DUP DTYPE  (PTYPE) ;
: (LQTYPE)  ( ADR CNT -- )
   2DUP #OUT @ -ROT <PQTYPE> #OUT !
   (QTYPE) ;
: (LEXPECT)  ( ADR CNT -- )
   OVER SWAP (EXPECT)
   #LP (CHKOUT) IOERR
   SPAN @ DTYPE BL DEMIT CLRCHN ;

SCR# 32
// CONSOLE PRINTER
: CONSOLE  ( -- )
   #LP CLOSE  CLRCHN
   ['] (EMIT) IS EMIT
   ['] (TYPE) IS TYPE
   ['] (QTYPE) IS QTYPE
   ['] (EXPECT) IS EXPECT ;
: PRINTER  ( -- )
   #LP CLOSE
   0 0  #LP DUP #LP2 (OPEN) IOERR
   ['] (PEMIT) IS EMIT
   ['] (PTYPE) IS TYPE
   ['] (PQTYPE) IS QTYPE
   ['] (EXPECT) IS EXPECT ;

SCR# 33
// LOGGER
: LOGGER  ( -- )
   PRINTER
   ['] (LEMIT) IS EMIT
   ['] (LTYPE) IS TYPE
   ['] (LQTYPE) IS QTYPE
   ['] (LEXPECT) IS EXPECT ;
CONSOLE

There are two variables to help with output formatting, #OUT and #LINE. PAGE turns both off. CR ( or 13 ( $D ) EMIT ) turns #OUT off and increments #LINE. Outputting other characters increments #OUT. (TYPE) JSR's into the main part of (EMIT) to maintain these variables.
DEMIT and DTYPE are versions of (EMIT) and (TYPE) that do not affect the formatting variables. They are for communicating with a device such as a disk drive.
QTYPE ( quote type) is for those occasions when listing a BLOCK or getting an INDEX ( showing the first line of a range of blocks ) and the block(s) in question may hold data rather than source code. QTYPE displays the control characters as reverse video characters ( or an underscore on the printer ) rather than allowing the control characters to affect the display.
(TYPE) and (QTYPE) call CLRCHN , to clear the channel, and PAUSE.
#OUT must only be incremented once for each character displayed ( or printed) and #LINE must only be incremented once for each carriage return sent or the output from something like WORDS would look rather strange.
Keep in mind that this I/O redirection worked great before the introduction of multitasking ( and works well with tasks that don't need to display anything.)
The programmer has typed LOGGER and pressed the enter key. TYPE and EMIT ( and couple of others ) are redirected to send output to both the screen and the printer ( the programmer wants a record of his ( or her) interactive Forth session). Now lets introduce multitasking. Here is a background task to print the time at the top right corner of the display. There are no fancy windows, this is a Commodore 64. Never mind that if I wanted the time displayed, I would write it directly to the screen with the task updating a small buffer with the correct screen data every minute, but copying that buffer to the correct screen address each time it ran ( or the time display would scroll away whenever the main task caused the screen to scroll.) In this example, the task uses TYPE to display the time. It uses XY and AT-XY to save, manipulate, and restore the cursor position. the problem is that each time the time display task updates the time display, a copy of the time is sent to the printer as well. If there is a semaphore to block access to the printer if another task is using it ( like the main task) then wouldn't this task just sit there and PAUSE each time it ran? If that is the case, then the time would never get displayed on the screen either. This defeats the purpose of having a time display task.
Whartung, If you have an elegant solution to this, I'd like to see it. Without code or diagrams I'm having a difficult time wrapping my mind around using semaphores to handle this.


Top
 Profile  
Reply with quote  
PostPosted: Thu Mar 21, 2019 11:45 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 858
In FORTH MULTITASKING IN A NUTSHELL, Brad Rodriguez mentions a rule: do not FORGET tasks!
I implemented my multitasker a bit differently than F83's.
The task creation word, TASK , does not link the new task into the round-robin list. A created task returns its address.
LINK-TASK is a word that takes the address of a task and links it into the round-robin list.
UNLINK-ALL is a word that empties the list, restoring it to its start-up condition. The desired tasks can then be linked into the list.
In Fleet Forth, since all tasks can be unlinked, a task can be safely forgotten but only if it is not linked.


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 25, 2019 9:00 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 858
I went ahead and wrote code for an auxiliary stack this weekend. The Y-register is the stack pointer, which is stored in memory. The fist thing the stack access words do is load the Y-register from that location and the last thing they do is store it back to that location. Since the only auxiliary stack operators I need transfer items between the data stack and the auxiliary stack, it can grow towards higher memory or towards lower memory. On one hand, I'm used to stacks that grow down toward lower memory like the data stack and return stack. On the other hand, the area of memory I'm using for the auxiliary stack is automatically cleared to all zeros by one of the Commodore 64 kernel routines that gets called during the cold start. A stack that grows toward higher memory would already be initialized by the cold start routine.
Here is prototype code of an auxiliary stack that grows toward higher memory. the stack pointer points to the next free byte.
Code:
SCR# 77
// AUX STACK
HEX
229 CONSTANT APOINTER
22B CONSTANT ABASE
: ADEPTH  ( -- N )
   APOINTER @ 2/ ;
: AP!  ( -- )  APOINTER OFF ;
: .AS  ( -- )
   ADEPTH DUP 0
   ?DO
      I 2* ABASE + @
      5 U.R SPACE
   LOOP
   ?EXIT
   ." EMPTY " ;

SCR# 78
// AUX STACK
HEX
CODE >A  ( S: N -- ) ( A: -- N )
   APOINTER LDY,
   0 ,X LDA,  ABASE ,Y STA,  INY,
   1 ,X LDA,  ABASE ,Y STA,  INY,
   APOINTER STY,
   POP JMP,  END-CODE
CODE A>  ( S: -- N ) ( A: N -- )
   APOINTER LDY,  DEX,  DEX,
   DEY,  ABASE ,Y LDA,  1 ,X STA,
   DEY,  ABASE ,Y LDA,  0 ,X STA,
   APOINTER STY,
   NEXT JMP,  END-CODE

SCR# 79
// AUX STACK
HEX
: 2>A  ( S: D -- ) ( A: -- D )
   >A >A ;
: 2A>  ( S: -- D ) ( A: D -- )
   A> A> ;
: CS>A  ( S: CS -- ) ( A: -- CS )
   2>A ; IMMEDIATE
: A>CS  ( S: -- CS ) ( A: CS -- )
   2A> ; IMMEDIATE

As is, the code has no bounds checking ( it is prototype code) and would have to be used as cautiously as the return stack. Bounds checking can be added because the auxiliary stack isn't as speed critical as the data stack ( it would probably only be used to manipulate control flow data during compiling or assembling.) The area of memory I've chosen has room for 23 16 bit cells or 11 items of control flow data.
If there is a use that would make it better to implement the Auxiliary stack so it grows toward lower memory, I would like to hear about it.


Top
 Profile  
Reply with quote  
PostPosted: Mon Mar 25, 2019 10:43 pm 
Online
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1929
Location: Sacramento, CA, USA
Forth 2012 defines 2>R as "Semantically equivalent to SWAP >R >R" but you have left out the SWAP in your 2>A. Is this because:
1) A grows upward,
2) You are blazing your own path,
3) Both of the above,
4) Neither of the above.

??

(I always thought that 2>R should have been called D>R because of the SWAP, but I have been mistaken on several occasions.)

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Tue Mar 26, 2019 9:02 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 858
barrym95838 wrote:
Forth 2012 defines 2>R as "Semantically equivalent to SWAP >R >R" but you have left out the SWAP in your 2>A. Is this because:
1) A grows upward,

Yes, because A grows upward. In the version where A grows downward, there is a SWAP in 2>A and 2A> . According to the Forth-83 Standard,
Quote:
Double numbers are represented on the stack
with the most-significant 16 bits (with sign) most
accessible. Double numbers are represented in memory by two
consecutive 16-bit numbers. The address of the least
significant 16 bits is two greater than the address of the
most significant 16 bits.

Also, I've noticed that 2>R and 2R> preserve the cell order of the double number.
Code:
: RTEST   1234.5678 2>R RP@ 1+ 10 DUMP 2R> 2DROP ;  OK
RTEST
 1F8  34 12 78 56 BC 21  9 22   C 30 30 2C 20 4F 4B 2C   4_.V_!_"_00, OK,
 OK
AP!  OK
1234.5678 2>A  OK
ABASE 10 DUMP
 22B  34 12 78 56  0  0  0  0   0  0  0  0  0  0  0  0   4_.V____________
 OK
1234.5678  OK
SP@ 10 DUMP
  7A  34 12 78 56  0  0  0  0   0  0  4 B6  0 D3 59 DD   4_.V_________.__
 OK

As can be seen in these memory dumps, the cell order ( and byte order) for the hexadecimal double number, 1234.5678, is the same on the data stack, the return stack, and the auxiliary ( or aux) stack.
Here is the code with some error checking added:
Code:
SCR# 77
// AUX STACK
HEX
229 CONSTANT APOINTER
22B CONSTANT ABASE
 2E CONSTANT APLIM
: ADEPTH  ( -- N )
   APOINTER @ 2/ ;
: AP!  ( -- )  APOINTER OFF ;
: .AS  ( -- )
   ADEPTH DUP 0
   ?DO
      I 2* ABASE + @
      5 U.R SPACE
   LOOP
   ?EXIT
   ." EMPTY " ;

SCR# 78
// AUX STACK
HEX
CODE >A  ( S: N -- ) ( A: -- N )
   APOINTER LDY,  APLIM 1- # CPY,
   0< NOT IF,
      >FORTH  AP!
      TRUE ABORT" AUX STACK FULL!"
      >ASSEM
   THEN,
   0 ,X LDA,  ABASE ,Y STA,  INY,
   1 ,X LDA,  ABASE ,Y STA,  INY,
   APOINTER STY,
   POP JMP,  END-CODE

SCR# 79
// AUX STACK
HEX
CODE A>  ( S: -- N ) ( A: N -- )
   APOINTER LDY,  2 # CPY,
   0< IF,
      >FORTH  AP!
      TRUE ABORT" AUX STACK EMPTY!"
      >ASSEM
   THEN,
   DEX,  DEX,
   DEY,  ABASE ,Y LDA,  1 ,X STA,
   DEY,  ABASE ,Y LDA,  0 ,X STA,
   APOINTER STY,
   NEXT JMP,  END-CODE

SCR# 7A
// AUX STACK
HEX
: 2>A  ( S: D -- ) ( A: -- D )
   >A >A ;
: 2A>  ( S: -- D ) ( A: D -- )
   A> A> ;
: CS>A  ( S: CS -- ) ( A: -- CS )
   2>A ; IMMEDIATE
: A>CS  ( S: -- CS ) ( A: CS -- )
   2A> ; IMMEDIATE

Quote:
(I always thought that 2>R should have been called D>R because of the SWAP, but I have been mistaken on several occasions.)

Well, maybe not because of the SWAP but because it is moving a double number to the return stack. I suppose the Standards team chose to prefix some of the double number words with a '2' instead of a 'D' to remind us that those double number words will also work with a pair of single numbers as well.


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 29, 2019 8:51 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 858
Here is the version of the Auxiliary stack where it grows toward lower memory. One of the Commodore 64 kernel routines called by the cold start routine initializes this area of memory ( among others) to all zeros. the constant APOINTER has been removed and ABASE has been moved down. The pointer now resides at ABASE and the lowest valid value for the pointer is 2. If the pointer value is zero ( like it will be when Fleet Forth first boots up) , >A and A> will "empty the aux stack".
Code:
SCR# 74
// AUX STACK
HEX
 30 CONSTANT APMAX
229 CONSTANT ABASE
: ADEPTH  ( -- N )
   APMAX  ABASE @  - 2/ ;
: AP!  ( -- )  APMAX ABASE ! ;
: .AS  ( -- )
   ADEPTH DUP 0
   ?DO
      APMAX ABASE + I 2* - 2-
      @ 5 U.R SPACE
   LOOP
   ?EXIT
   ." EMPTY " ;

SCR# 75
// AUX STACK
HEX
CODE >A  ( S: N -- ) ( A: -- N )
   ABASE LDY,
   0= IF,  APMAX # LDY,  THEN,
   4 # CPY,
   0< IF,
      >FORTH
      TRUE ABORT" AUX STACK FULL!"
      >ASSEM
   THEN,
   DEY,  1 ,X LDA,  ABASE ,Y STA,
   DEY,  0 ,X LDA,  ABASE ,Y STA,
   ABASE STY,
   POP JMP,  END-CODE

SCR# 76
// AUX STACK
HEX
CODE A>  ( S: -- N ) ( A: N -- )
   ABASE LDY,
   0= IF,  APMAX # LDY,  THEN,
   APMAX 1- # CPY,
   0< NOT IF,
      >FORTH
      TRUE ABORT" AUX STACK EMPTY!"
      >ASSEM
   THEN,
   DEX,  DEX,
   ABASE ,Y LDA,  0 ,X STA,  INY,
   ABASE ,Y LDA,  1 ,X STA,  INY,
   ABASE STY,
   NEXT JMP,  END-CODE

SCR# 77
// AUX STACK
HEX
: 2>A  ( S: D -- ) ( A: -- D )
   SWAP >A >A ;
: 2A>  ( S: -- D ) ( A: D -- )
   A> A> SWAP ;
: CS>A  ( S: CS -- ) ( A: -- CS )
   2>A ; IMMEDIATE
: A>CS  ( S: -- CS ) ( A: CS -- )
   2A> ; IMMEDIATE

You may notice that the error handling does not clear the auxiliary stack. I thought it would be better if the auxiliary stack was cleared on all errors. In Fleet Forth, (ABORT") , the word compiled by ABORT" , calls ABORT. ABORT is defined like this:
Code:
: ABORT  ( -- )
   ERR SP! QUIT ; -2 ALLOT

ERR is a deferred word that normally is set to NOOP , a no-op. The phrase -2 ALLOT is to reclaim memory used by an EXIT that will never be reached ( nothing comes back from QUIT .)
The auxiliary stack can be set up to be cleared on any error ( any ABORT" or ABORT ) with the following:
Code:
' AP! IS ERR


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 03, 2019 8:26 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 858
I added to my system loader the version of the Auxiliary stack that grows toward lower memory. It seemed like a better fit with all three stacks growing toward lower memory. I added some code to my kernel to clear the Auxiliary stack on error ( and cold start.)
Now I get to play around with it and see just how useful it really is. :D


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 03, 2019 9:01 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 858
I've mentioned before some of the Forth-83 Standard words in Fleet Forth, but to be more specific it has:
1) All of the Required Words.
2) All of the Double Number Extension Words.
3) All of the System Extension Words.
4) All of the Assembler Extension Words.
5) All but four words from the Controlled Reference Words.
6) And some of Uncontrolled Reference Words.

These are the four words not included from the Controlled Reference Words:
--> I think that THRU is better especially since on my system a colon definition can cross a block boundary ( I dislike that extremely dense horizontal formatting.)
END This is just a synonym for UNTIL .
OCTAL I don't really see a need for this one.
These three words can be defined anytime they are needed. The fourth word I didn't include from the Controlled Reference Words is OFFSET . It would have to be defined in the kernel because BLOCK would need to access it. I didn't include it because I can't really think of a situation where it would be very useful.


Top
 Profile  
Reply with quote  
PostPosted: Fri Apr 05, 2019 9:38 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 858
Want to see something scary? With Fleet Forth I can do this.
Code:
0 >R CR .S CR .AS R> CR . CR
EMPTY
EMPTY
0
 OK

Here is using the much safer Auxiliary stack.
Code:
0 >A CR .S CR .AS A> CR . CR
EMPTY
    0
0
 OK

Warning! The above ( using the return stack ) is not portable! It works on Fleet Forth because it ( what was typed in ) is all on one line and INTERPRET calls EXECUTE directly instead of through some other word. It would also work if it was all in one EVALUATEed string or one screen ( BLOCK ) of source.

[Edit: one clarification]


Last edited by JimBoyd on Sun Jun 09, 2019 1:31 am, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Sat Apr 06, 2019 12:50 am 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 858
JimBoyd wrote:
I added to my system loader the version of the Auxiliary stack that grows toward lower memory.

Taking multitasking into consideration, background tasks do not get their own Auxiliary stack or even a portion of the Auxiliary stack. For the purposes I envisioned, only the main task would need to use the Auxiliary stack ( help with managing control flow in the assembler and metacompiler) but this may need further investigation.
Any suggestions regarding the use of an Auxiliary stack by a background task?


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 10, 2019 7:46 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 858
I'm working on Fleet Forth's Kernel and I'm dealing with a matter of commenting vs. constants when using the C64 Kernal Jump Table. The C64 Jump Table isn't going anywhere and the constants I'm using are only defined in the host Forth dictionary so it is a matter of readability. Which seems more readable?
this:
Code:
HEX
CODE CLOSE  ( N -- )
   0 ,X LDA,
   XSAVE STX,
   0FFC3 JSR,  // CLOSE
   XSAVE LDX,
   POP JMP,  END-CODE

or having this near the beginning of the source
Code:
HEX
   .
   .

FFC3 DEFINE C64.CLOSE
   .
   .

and this later in the source?
Code:
HEX
CODE CLOSE  ( N -- )
   0 ,X LDA,
   XSAVE STX,
   C64.CLOSE JSR,
   XSAVE LDX,
   POP JMP,  END-CODE


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 10, 2019 8:14 pm 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
If there are a LOT of those constants, I'd use the // CLOSE comment method rather than pollute the dictionary (any dictionary) with one-time constants. That could be a measurable amount of memory tied up in constants and dictionary space, with no real long term value (IMHO).


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 10, 2019 8:27 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 858
Only about 18 ( yeah, that is a lot).
Thanks, I'll reserve my one time constants for things in the design of Fleet Forth which could change.


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

All times are UTC


Who is online

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