Jump to content
IGNORED

TurboForth V1.2 (Beta)--Evolution & Arcana


Lee Stewart

Recommended Posts

Possibly! If could remember how fixSP works ;-)

 

Does it get the address from a vector or is hard-coded? It would be better if FP AL routines restored the scratch-pad environment prior to returning to Forth, IMHO. It would certainly be faster.

 

I guess you are looking for a definitive answer. Please proceed along the lines of AL routines restoring scratch-pad themselves by means of the built in subroutine in the TF ROM.

 

So, yes, the FP Forth code would change in so far as calls to fixSP would be removed, and fixSP itself would be eliminated.

 

Mark

 

It gets the address from the same vector. I asked the question because the two actions are very different. fixSP uses it as the address from which to start copying 34 bytes to restore TF's scratchpad RAM. The routine you just posted branches to code at the vectored address. Of course, the code I'm looking at for fixSP is TF Assembler code that may not be up to date. Your last CODEd fixSP in the FP package may be different. I did not disassemble it to find out.

 

...lee

Link to comment
Share on other sites

Wow Lee! Excellent work on spotting that one! You're right, the build I posted earlier today contained the wrong address in the vector. It was pointing to the scratchpad code itself, rather than the code to restore the scratch-pad. Doh.

 

Please use the build below. It loads PADVEC (>A010) with the address >6AEC (do $A010 @ $. to confirm). I can confirm categorically that the address held in PADVEC is the address of *code* in bank1 that restores the scratch-pad code. The code returns to the caller with an RT instruction. See the end of the file 1-06-Blocks.a99

 

New build and zipped source code attached. Apologies for any confusion caused! Sometimes I get confused myself! :dunce:

 

Mark

TF-V1.2-12-OCT-2012-II.zip

TF-V1.2-Source-12-Oct-2010.zip

Link to comment
Share on other sites

Wow Lee! Excellent work on spotting that one! You're right, the build I posted earlier today contained the wrong address in the vector. It was pointing to the scratchpad code itself, rather than the code to restore the scratch-pad. Doh.

 

Please use the build below. It loads PADVEC (>A010) with the address >6AEC (do $A010 @ $. to confirm). I can confirm categorically that the address held in PADVEC is the address of *code* in bank1 that restores the scratch-pad code. The code returns to the caller with an RT instruction. See the end of the file 1-06-Blocks.a99

 

New build and zipped source code attached. Apologies for any confusion caused! Sometimes I get confused myself! :dunce:

 

Mark

 

That does mean, then, that fixSP will have to be changed in the FP package because I am pretty sure that it is copying code itself using that vector. Either that or managing the returns fron xmllnk and gpllnk some other way as you suggested with dsrlnk. Actually, that may not be possible until v2.0 with the possible inclusion of the MDOS-derived package because the contents of 8354h (FP error) has to be captured before restoring TF.

 

...lee

Link to comment
Share on other sites

One solution to that is to simply have the FP routines push >8354 to the data stack, or, if that's inconvenient, to a Forth variable.

 

VARIABLE FP_ERR
ASM: SomeFPFunction
...
... <fp incantations!>
...
$8354 @@ FP_ERR @@ MOV, \ load variable with fp error code
;ASM

 

Hmmm... You're not using a Forth assembler, though are you...? So, if you don't want to push the FP error code to the stack every time (and I can't blame you for that) then you'd need a word to initialise the 'bridge' between the FP assembly code and Forth. It may do nothing more than tell the assembly routines the address of the FP_ERR variable:

 

VARIABLE FP_ERR
: FP_INIT ( 'fp_err -- ) $xxxx ! ;

FP_ERR FP_INIT

 

Here, FP_INIT takes the address of FP_ERR and supplies it to the FP code (by poking an address (probably a hard coded address) which is known to your machine code).

Link to comment
Share on other sites

One solution to that is to simply have the FP routines push >8354 to the data stack, or, if that's inconvenient, to a Forth variable.

 

VARIABLE FP_ERR
ASM: SomeFPFunction
...
... <fp incantations!>
...
$8354 @@ FP_ERR @@ MOV, \ load variable with fp error code
;ASM

 

Hmmm... You're not using a Forth assembler, though are you...? So, if you don't want to push the FP error code to the stack every time (and I can't blame you for that) then you'd need a word to initialise the 'bridge' between the FP assembly code and Forth. It may do nothing more than tell the assembly routines the address of the FP_ERR variable:

 

VARIABLE FP_ERR
: FP_INIT ( 'fp_err -- ) $xxxx ! ;

FP_ERR FP_INIT

 

Here, FP_INIT takes the address of FP_ERR and supplies it to the FP code (by poking an address (probably a hard coded address) which is known to your machine code).

 

But, I am using the Forth assembler when needed. You may recall that I rewrote your gpllnk for the transcendentals. I will use whatever we need to use. I actually thought of the data stack solution, but there seemed to be too much work otherwise to shoehorn that in. The problem with putting the fixSP code inside of gpllnk is, I think, that gpllnk can be used for functions that don't use the FP error location.

 

...lee

Link to comment
Share on other sites

Hmmm... Okay, I think I see what you are saying. I was assuming here that transcendentals, and the FP library will be dumped and re-implemented using your FP library. That was certainly my intention. :-D So GPLLNK wouldn't be called at all. It wouldn't even be present!

Link to comment
Share on other sites

Yes I would. I think it would be a great feature, for both TIF and TF. There's really nothing wrong with TI FP stuff in terms of their accuracy, but interfacing to them in AL is a PITA, as we know! The big issue is the pesky 256 bytes of pad. Why oh why couldn't they have put more RAM in there?! :mad:

Link to comment
Share on other sites

Yes I would. I think it would be a great feature, for both TIF and TF. There's really nothing wrong with TI FP stuff in terms of their accuracy, but interfacing to them in AL is a PITA, as we know! The big issue is the pesky 256 bytes of pad. Why oh why couldn't they have put more RAM in there?! :mad:

 

OK, Chief, I'll get on it right away! Well, after I get back from yet another party! Maybe, tomorrow...

 

...lee

Link to comment
Share on other sites

Floating Point Library---

 

OK, here's the TF assembler code for DSRLNK to invoke the cartridge ROM dsrlnk. Whatever this shakes out as could be used for other applications of dsrlnk; but, here I am only interested in using it to load the memory image of the FPL. Then, I can FORGET the first word that is used only for the image load, NL_PTR in this case, before going on to load the TF FP package:

 

 

\ Prior to calling DSRLNK,

\ 1. A PAB must be set up in VDP RAM,

\ 2. A pointer (NL_PTR) to PAB+9 (filename length byte)

\ shoud be set up to pass into DSRLNK and

\ 3. The subroutine number (>8 or >A) should be on the stack.

 

0 VALUE NL_PTR

0 VALUE DSR_ERR

ASM: DSRLNK ( subr -- )

$6000 @@ CLR, \ select bank 1

NL_PTR @@ $8356 @@ MOV, \ PAB+9 pointer

R7 $0420 LI, \ construct BLWP instruction

$A00C @@ R8 MOV, \ to DSRLNK

*SP+ R9 MOV, \ pop DSR subroutine (8 or Ah) from stack

R10 $045B LI, \ construct B *Rll instruction

$830E @@ BL, \ execute above instructions at R7

$837C @@ R0 MOVB, \ get I/O error

R0 $20 ANDI, \ pick off COND bit

R0 DSR_ERR @@ MOV, \ move I/O error to DSR_ERR

$A010 @@ R0 MOV, \ read pointer to scratchpad restore code

R0 ** BL, \ restore scratchpad

$6002 @@ CLR, \ select bank 0

;ASM \ back to Forth

 

 

 

The code that actually executes the branch to dsrlnk in cartridge ROM space is put in R7-R10, which is then launched. R11 gets used in the return from that code. Next, the error return is jammed into DSR_ERR to check for a DSR error. Perhaps I should also check the status byte of the PAB for specific file I/O errors. Finally, scratchpad RAM is restored before returning to TF.

 

Anyway, the point of this post, besides showing you where I am with the FPL, is to get feedback on how this may need to be changed to get the desired result. You see, I haven't actually run it yet. :D

 

...lee

Edited by Lee Stewart
Link to comment
Share on other sites

Hi Lee,

 

Nice job. I like the technique of executing code from within the register workspace. I also use that very same technique. In fact that is how I was able to add the ability to change TF's workspace on the fly (added in the last but one build, IIRC).

 

Looking at your code, I think NL_PTR and DSR_ERR need to be VARIABLEs, not VALUEs. You see, your AL code is reffering to the addresses of the bodies of those words, as if they were variables. However, since they are values they will actually supply their *values* to the AL code, which is zero, which is not what you want! You need variables!

 

I haven't studied the rest in mega detail yet but I thought I would post this as it just 'leapt' out at me from the screen, and it's the sort of bug that could take a while to track down (though Classic99's debugger would soon show you the error of your ways!) leading to a forehead slapping moment :)

Link to comment
Share on other sites

Okay, an additional observation:

 

I could be wrong, but this phrase doesn't look quite right:

 

$837C @@ R0 MOVB, \ get I/O error

R0 $20 ANDI, \ pick off COND bit

R0 DSR_ERR @@ MOV, \ move I/O error to DSR_ERR

 

You're using a MOVB instruction (high byte) but your ANDI works on the low byte. Shouldn't the ANDI use an argument of >2000?

If so, you will probably want to use an extra SWPB to move the result to the low byte just before you write it to the user variable on the Forth side - it's more convenient for the Forth programmer to have the error code 'right justified' IMHO.

 

Mark

Link to comment
Share on other sites

One last comment, which isn't so useful, but is maybe worth considering:

 

It should be possible to BSAVE your assembly code (and therefore BLOAD it) thereby not needing DSRLNK at all. This might make it easier during development, as you could assemble the code using the Forth assembler and BSAVE it, rather than using editor assembler, assemble it, create an option 5 etc etc.

 

It's possible to manipulate HERE in TF by means of the variable H. Note: writing to HERE does nothing, but writing to H changes HERE. So:

 

$2000 H !

 

And we're now compiling to low memory. If you display the variable FFAILM (First free address in low memory) before and after compiling some code you'll see the value change. Also the word MEM is useful (shows free bytes low, high, and total free bytes respectively).

 

BSAVE saves from a start address that you supply, to HERE. So, having loaded your Forth assembler code via a block, you could do:

 

$2000 <block> BSAVE

 

and save the assembly code (in binary format) to a block(s) of your choosing. Now, (as you know) the assembler is not required to be in memory when loading the assembly code back into memory.

 

It should be possible to script the manipulation of HERE during load time from a block; I've never tried it, I must give it a try. If it doesn't work then it strikes me there's a bug hidden somewhere. Anyway, it should be possible, in a block, to do something like this:

 

HERE \ save HERE

$2000 H ! \ set HERE

67 BLOAD DROP \ load the FP library into low memory

H ! \ restore HERE

 

Now your assembly code is in low memory, without the need for a binary loader, and high-level Forth code will be compiled into high memory, and the dictionary will link up just fine.

 

As I say, it's six and two three's really. I just thought it might make the development cycle a little easier! Personally, that's how I would do it; I'd leverage the Forth environment as much as possible. As you know, it's pretty cool to be able to just type in assembly code and run it immediately! All the cart swapping (even on an emulator) is a pain, and waiting for editor assembler etc..... Blah... I don't think I'd have the patience to write an assembly application on the real hardware any more!

 

FWIW :)

 

Mark

Link to comment
Share on other sites

Hi Lee,

 

Nice job. I like the technique of executing code from within the register workspace. I also use that very same technique. In fact that is how I was able to add the ability to change TF's workspace on the fly (added in the last but one build, IIRC).

 

Looking at your code, I think NL_PTR and DSR_ERR need to be VARIABLEs, not VALUEs. You see, your AL code is reffering to the addresses of the bodies of those words, as if they were variables. However, since they are values they will actually supply their *values* to the AL code, which is zero, which is not what you want! You need variables!

 

I haven't studied the rest in mega detail yet but I thought I would post this as it just 'leapt' out at me from the screen, and it's the sort of bug that could take a while to track down (though Classic99's debugger would soon show you the error of your ways!) leading to a forehead slapping moment :)

 

Yeah, I don't know what I was thinking!

 

...lee

Link to comment
Share on other sites

Okay, an additional observation:

 

I could be wrong, but this phrase doesn't look quite right:

 

$837C @@ R0 MOVB, \ get I/O error

R0 $20 ANDI, \ pick off COND bit

R0 DSR_ERR @@ MOV, \ move I/O error to DSR_ERR

 

You're using a MOVB instruction (high byte) but your ANDI works on the low byte. Shouldn't the ANDI use an argument of >2000?

If so, you will probably want to use an extra SWPB to move the result to the low byte just before you write it to the user variable on the Forth side - it's more convenient for the Forth programmer to have the error code 'right justified' IMHO.

 

Mark

 

Thanks, again! I was in the C@ mode, which word does an 8-bit right shift. That said, I think using " R0 $2000 ANDI, " should work fine because I am only concerned about a non-zero value for DSR_ERR to indicate whether there was an error. H-m-m-m, with that in mind, would it be quicker to use " $837C @@ R0 MOV, " than " $837C @@ R0 MOVB, "? After all, the extra byte is masked out.

 

...lee

Edited by Lee Stewart
Link to comment
Share on other sites

One last comment, which isn't so useful, but is maybe worth considering:

 

It should be possible to BSAVE your assembly code (and therefore BLOAD it) thereby not needing DSRLNK at all. This might make it easier during development, as you could assemble the code using the Forth assembler and BSAVE it, rather than using editor assembler, assemble it, create an option 5 etc etc.

...

FWIW :)

 

Mark

 

This would be an excellent way to do it were I starting from scratch. Here, however, I would need to translate 500 lines of ALC to TF ALC---a bit of a mind-numbing prospect. I will eventually get it down to under 400 lines---maybe even less then 350---still a daunting task.

 

...lee

Link to comment
Share on other sites

Floating Point Library---

 

OK, here's my latest attempt at DSRLNK to call into TF cartridge ROM's dsrlnk. It includes all of @Willsy's corrections and an optional method for executing the register code, which may need correcting because I'm not sure I have it right:

 

 

 

\ Prior to calling DSRLNK,

\ 1. A PAB must be set up in VDP RAM,

\ 2. A pointer (NL_PTR) to PAB+9 (filename length byte)

\ shoud be set up to pass into DSRLNK and

\ 3. The subroutine number (>8 or >A) should be on the stack.

 

VARIABLE NL_PTR

VARIABLE DSR_ERR

ASM: DSRLNK ( subr -- )

$6000 @@ CLR, \ select bank 1

NL_PTR @@ $8356 @@ MOV, \ PAB+9 pointer

R7 $0420 LI, \ construct BLWP instruction

$A00C @@ R8 MOV, \ to DSRLNK

*SP+ R9 MOV, \ pop DSR subroutine (8 or Ah) from stack

R10 $045B LI, \ construct B *Rll instruction

$830E @@ BL, \ execute above instructions at R7

 

\ Maybe should use the following two instructions in place of the

\ one above to allow for calling DSRLNK with a different workspace:

\ R0 STWP, \ get WP

\ 2 R7 * R0 ** + @@ BL, \ execute above instructions at R7

 

$837C @@ R0 MOV, \ get I/O error

R0 $2000 ANDI, \ pick off COND bit

R0 DSR_ERR @@ MOV, \ move I/O error to DSR_ERR

$A010 @@ R0 MOV, \ read pointer to scratchpad restore code

R0 ** BL, \ restore scratchpad

$6002 @@ CLR, \ select bank 0

;ASM \ back to Forth

 

 

 

...lee

Link to comment
Share on other sites

Lee,

 

Here's a tested version of your DSRLNK code. I had it loading a file in the Classic99 folder called DEMO (8K long). There were a couple of bugs but nothing you wouldn't have found yourself in less than a minute.

 

I've also added a wrapper, called OPT5 which builds the PAB and calls DSRLNK. Use it like this:

 

S" DSK1.DEMO" OPT5

 

Your file should be loaded into VDP starting at >1018.

 

That's as far as the code goes. The next step (for in the morning - unless you fancy a hack at it! ;) ) is to add a little bit of code to OPT5 to move the code from VDP to the correct place in CPU ram, which is very easy, just make use of the first 6 bytes loaded into VDP, which tells you all you need to know.

 

OBSERVATION: I tested it with a file called DEMO in the DSK1 folder of classic99. I think you'll have the same file unless you have deleted it. It DOES load the file into VDP (check VDP in the debugger at >1018 and also the messages in the debugger; there are no errors reported in the classic99 debug window). However, the COND bit always comes back as a 1. No idea why. Maybe it's normal?

 

 

 

 

VARIABLE NL_PTR

VARIABLE DSR_ERR

ASM: DSRLNK ( subr -- )

DSR_ERR @@ CLR, \ zero DSR_ERR

$6000 @@ CLR, \ select bank 1

NL_PTR @@ $8356 @@ MOV, \ address of name length byte to >8356

R7 $0420 LI, \ construct BLWP instruction

$A00C @@ R8 MOV, \ to DSRLNK

*SP+ R9 MOV, \ pop DSR subroutine (8 or Ah) from stack

R10 $045B LI, \ construct B *Rll instruction

 

R0 STWP,

R0 14 AI, \ R0 points to the address of R7

R0 ** BL,

 

$837C @@ R0 MOVB, \ get I/O error

R0 $2000 ANDI, \ pick off COND bit

R0 8 SRL, \ move error code to low byte

R0 DSR_ERR @@ MOV, \ move I/O error to DSR_ERR

$A010 @@ R0 MOV, \ read pointer to scratchpad restore code

R0 ** BL, \ restore scratchpad

$6002 @@ CLR, \ select bank 0

;ASM \ back to Forth

 

CREATE PAB_BUF 10 CHARS ALLOT

: OPT5 ( "filename" -- )

FLUSH \ purge disk blocks

$1000 >R \ address of PAB in VDP

PAB_BUF 10 0 FILL \ zero pab

5 PAB_BUF C! \ load op-code to PAB byte 0

R@ 24 + PAB_BUF 2+ ! \ vdp address of loaded data in bytes 2 & 3

$2000 PAB_BUF 6 + ! \ maxi bytes to load in PAB bytes 6 & 7

DUP PAB_BUF 9 + C! \ length of file name to PAB byte 9

R@ 9 + NL_PTR ! \ set NL_PTR with vdp addr of length byte

R@ PAB_BUF 10 VMBW \ write pab to VDP

R> 10 + -ROT VMBW \ write filename to PAB

8 DSRLNK \ go load the file

DSR_ERR @ ?DUP IF

CR ." Disk error #" $. \ abort if error

THEN

;

S" DSK1.DEMO" OPT5

 

 

 

Finally, posted as a simple text file for your convenience.

 

Also, there's a very good chance that a loaded file will spill into the TF block buffers in VDP. Hence the FLUSH in OPT5 before we get funky with the loader. VDP memory map attached for your added enlightenment ;)

 

Enjoy :-D :thumbsup:

 

Mark.

DSRLNK.txt

Memory Map.zip <-- this is for V1.1 but VDP memory has not changed IIRC!

 

[EDIT: I've *no* idea how to preserve the freaking spacing in posted code. Please share your secret with me!]

Link to comment
Share on other sites

Lee,

 

Here's a tested version of your DSRLNK code. I had it loading a file in the Classic99 folder called DEMO (8K long). There were a couple of bugs but nothing you wouldn't have found yourself in less than a minute.

 

I've also added a wrapper, called OPT5 which builds the PAB and calls DSRLNK.

 

Thanks! I'll have a look later tonight.

 

...

However, the COND bit always comes back as a 1. No idea why. Maybe it's normal?

 

H-m-m-m, maybe it (837Ch) needs to be cleared first?

 

 

...

Also, there's a very good chance that a loaded file will spill into the TF block buffers in VDP. Hence the FLUSH in OPT5 before we get funky with the loader. ...

 

I haven't looked at OPT5, yet; but, I implied by the name that you think it's an E/A-option-5 file, which it's not. It is a memory image, however. It was saved as a normal object file, loaded, my FPLSAVE object code was loaded, FPLSAVE was run. Running FPLSAVE saves the memory image from 2700h- 3DF5h to DSK2.FPL.

 

Enjoy :-D :thumbsup:

 

Mark.

[EDIT: I've *no* idea how to preserve the freaking spacing in posted code. Please share your secret with me!]

 

I only manage the indents. The AA editor screws up any other spacing I attempt! The part I want indented I set off with blank lines, block the lines to be indented, hit the "right indent" icon on the toolbar, remove the set-off lines. It seems to keep the indents.

 

...lee

Link to comment
Share on other sites

Thanks! I'll have a look later tonight.

H-m-m-m, maybe it (837Ch) needs to be cleared first?

 

A few thoughts:

 

1. Most DSRLNK routines reset the status bit via R15 of the caller's WS upon entry. Error codes are usually returned either in PAB byte 1 (level 3 IO routines) or 0x8350 (level 1 and 2 routines). The status bit compliments the error byte in determining the type of error. The two docs I reference in the FIXED file thread may be of some value to you. Note: many DSRLNK routines don't test the type of call (8 vs. A) and end up reporting errors for the latter type that aren't truly errors.

 

2. DSRLNK sets the status bit via the caller's workspace registers. Does this translate into setting/resetting the 837c status bit, ie, are you really testing the status bit you think you're testing?

 

3. Many (all?) peripheral cards expect DSRLNK to branch to their respective on-card routines using workspace 0x83E0. Strange things happen otherwise. I suspect the routine you are calling follows this convention.

Link to comment
Share on other sites

A few thoughts:

 

1. Most DSRLNK routines reset the status bit via R15 of the caller's WS upon entry. Error codes are usually returned either in PAB byte 1 (level 3 IO routines) or 0x8350 (level 1 and 2 routines). The status bit compliments the error byte in determining the type of error. The two docs I reference in the FIXED file thread may be of some value to you. Note: many DSRLNK routines don't test the type of call (8 vs. A) and end up reporting errors for the latter type that aren't truly errors.

 

I had forgotten that level 1 and 2 disk DSR routines use FAC for the transfer block and 8350h of that area to return errors. Perhaps I should make DSRLNK explicitly level 3 ( DSRLNK call 8 ). TF doesn't use any level 1 or 2 calls anyway. That was a carryover from TI Forth.

 

2. DSRLNK sets the status bit via the caller's workspace registers. Does this translate into setting/resetting the 837c status bit, ie, are you really testing the status bit you think you're testing?

 

3. Many (all?) peripheral cards expect DSRLNK to branch to their respective on-card routines using workspace 0x83E0. Strange things happen otherwise. I suspect the routine you are calling follows this convention.

 

837Ch is the GPL status byte, which is checked by TI Forth's level 3 calls to DSRLNK , (it does not use the GPL's DSRLNK) though I cannot seem to find where that location gets set by TI Forth's DSRLNK , by Paolo Bagnaresi's practically identical dsrlnk used by TurboForth or by the DSR that's disassembled on Thierry's website. Both of the dsrlnk routines do, in fact, use the GPL workspace at 83E0h from where they start checking the DSR for the subroutine name until they return, unless there's an error, in which case they set, as you say, R15 (to 2000h) and the caller's R0's high byte to the PAB (PAB+1) or Transfer Block's (8350h) STATUS byte's first 3 bits shifted to the rightmost 3 bits. The complementarity of the COND bit with the PAB status byte (PAB byte 1) is significant IIRC only if the GPL COND bit = 0. If it's 1, it indicates there was a device error. I am also pretty sure that any access to GPL ought to first clear that byte; hence, my question. But apparently, I was laboring under a delusion, which raises the question as to why TI Forth's file management routine CHK-STAT checks the GPL STATUS byte. :?

 

I guess the bottom line is that, though the file error code can be extracted from PAB+1 or 8350h depending on the call, R0 should already have the error code regardless of subroutine and the status register has the EQ bit set if there was an error.

 

...lee

Edited by Lee Stewart
Link to comment
Share on other sites

I guess the bottom line is that, though the file error code can be extracted from PAB+1 or 8350h depending on the call, R0 should already have the error code regardless of subroutine and the status register has the EQ bit set if there was an error.

 

...lee

 

I think that's the long and the short of it, Lee, yes. Certainly, if I have interpreted Paolo's DSRLNK correctly, (which TF uses) then the error code if any is in R0. That's how the higher level code is written in TF, such as BLOCK. You'll see sequences such as:

 

BLWP @DSRLNK

DATA 8

JNE ERROR

 

Since R0 will be non-zero on an error.

 

I've posted the blocks source file below, which includes the DSRLNK code (scroll to the bottom). InsaneMultitasker, I'd be grateful if you could cast your eye over it too, and give your interpretation. There's some code towards the end which deals with DSRLNK code 10 calls, but TF only uses code 8 calls.

 

Thanks!

 

Mark

1-06-Blocks.zip

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...