+Lee Stewart Posted March 2, 2014 Author Share Posted March 2, 2014 Because this first cartridge version is about making it as easy on myself as possible, I suppose I could put the two fbForth resident vocabulary words, FORTH and ASSEMBLER , into the A000h+ space to avoid modifying them at this time. The reason for a future mod is that they harbor space that gets modified. ...lee Quote Link to comment Share on other sites More sharing options...
Omega-TI Posted March 10, 2014 Share Posted March 10, 2014 I found something on another part of Atari Age that you Forth guys might like. It was originally posted by: ThumpNugget on August of 2010 the entire issue of Byte magazine - The Forth Language << CLICK HERE >> It's HUGE (308 pages and 207 megabytes) It'll take a few minutes to download... Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 10, 2014 Author Share Posted March 10, 2014 I found something on another part of Atari Age that you Forth guys might like. It was originally posted by: ThumpNugget on August of 2010 the entire issue of Byte magazine - The Forth Language ... Thanks! I will definitely take a look. The cover art is the same as what appeared the next year on the cover of R. G. Loeliger's Threaded Interpretive Languages, published by BYTE Books and hyped on p. 134 of this issue of BYTE. I bought the book in 1983 or 1984 after getting TI Forth from the Washington, DC Area Users Group. ...lee Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 16, 2014 Author Share Posted March 16, 2014 fbForth minutiae— I am attempting to make the ALC of the resident dictionary more readable and easier to maintain. I have continued to use the original TI Forth format, but I'm thinking of changing it for the reasons stated. Here is the current code snippet for EMPTY-BUFFERS , one of the longest word names in fbForth: * *** EMPTY-BUFFERS *** DATA L10FC L10FE DATA >8D45,>4D50,>5459,>2D42,>5546,>4645,>52D3 EMPTYB DATA DOCOL,FIRST,LIMIT,OVER,SUB,ERASE,FLUSH DATA FIRST,USE,STORE,FIRST,PREV,STORE,SEMIS The name field (labeled L10FE for the above ALC) is complicated by bit markers that are part of the leading and trailing bytes, which makes it difficult to use ALC's TEXT directive in a clear manner. I think the following may be as clear as I can get: * *** EMPTY-BUFFERS *** DATA L10FC L10FE DATA >8000->2000+>0D00+' E','MP','TY','-B','UF','FE','RS'+>0080 EMPTYB DATA DOCOL,FIRST,LIMIT,OVER,SUB,ERASE,FLUSH DATA FIRST,USE,STORE,FIRST,PREV,STORE,SEMIS The first DATA word of the name field has 3 additional terms, viz., markers (in this case, +>8000), ->2000 (corrects space in first byte to null) and +>0x00 (x = character count always in first byte). The last DATA word of the name field has only the >0080 marker. This format is a little messy, but it does allow for fair readability of the source code. Anybody have a better idea? ...lee Quote Link to comment Share on other sites More sharing options...
Willsy Posted March 17, 2014 Share Posted March 17, 2014 (edited) Yeah! Junk the format and change FIND et al to use the new format! TF's format is okay I think. Here's the guts of an old datasheet that I put together back in 2009 describing it: The dictionary in TurboForth is a linked list, with the first entry in a dictionary entry being a pointer to the *previous* entry. The dictionary is searched (by FIND) from the most recent entry to the first entry, i.e the dictionary is searched in reverse order. The first entry in a dictionary entry is a pointer to the previous dictionary entry. It is a 16-bit word. The next 16-bit word in the dictionary entry contains: * Length of the name of the word (bits 12 to 15) * Block number that the word is defined in (bits 2 to 11) * Hidden flag (bit 1) * Immediate flag (bit 0) as seen below: 0 15 MSB LSB x x x x x x x x x x x x x x x x | | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~ | | | | | | block number length | hidden immediate The block number is encoded into the word by HEADER during LOADing of a block. This allows the word to be easily located (if it has been loaded) by use of the word WHERE - e.g. WHERE WILLS Note that since the name of the word can only be 4 bits, the maximum length of a word is 15 characters. The next entry in the dictionary entry is the actual text of the word name. One byte per character, in normal ASCII. The word is padded with a space or zero if the word is an odd length. The next entry is the CFA (Code Field Address). This is a pointer to the machine code that will be executed when the word is executed. For primitive words, the value of the word will be address of the word plus 2 (i.e it points to the word immediately following it). For Forth (i.e. high level) words it points to DOCOL. If the following words were entered at the keyboard immediately after booting: (i.e RAM is empty) : MARK ; : WILLS ; The dictionary entries would look like this: Address Value -------------- +-> A302 7F04 * POINTER TO PREVIOUS WORD IN CART ROM | A004 0004 * LENGTH OF THE NAME 'MARK' | A006 4D M | A007 41 A ^ A008 52 R | A009 4B K | A00A 8320 * POINTER TO DOCOL IN PAD RAM | A00C 832E * POINTER TO EXIT IN PAD RAM | +-< A00E A302 * POINTER TO PREVIOUS DICTIONARY ENTRY A010 0005 * LENGTH OF WORD 'WILLS' A012 57 W A013 49 I A014 4C L A015 4C L A016 53 S A017 00 * PAD TO EVEN ADDRESS A018 8320 * POINTER TO DOCOL IN PAD RAM A01A 832E * POINTER TO EXIT IN PAD RAM Edited March 17, 2014 by Willsy Quote Link to comment Share on other sites More sharing options...
Willsy Posted March 17, 2014 Share Posted March 17, 2014 I have some equates set up in the TF source for immediate and hidden words, which makes things quite readable. Here's the word --> which is immediate: ; --> ( -- ) ; loads the next block nblkh data blkh,immed+3 text '--> ' nblk data docol data lit,lstblk,fetch,plus1,block,tib_,store,in_,store0 data exit ;] Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 17, 2014 Author Share Posted March 17, 2014 Yeah! Junk the format and change FIND et al to use the new format! TF's format is okay I think. Here's the guts of an old datasheet that I put together back in 2009 describing it: Thanks for the suggestion; but, I really cannot modify the format of the fbForth dictionary. That part I really do need to keep compatible with TI Forth. Your suggestions would be too big a change, I'm afraid. In fbForth/TI-Forth, there is a terminator bit (80h) at the beginning of the length byte and at the beginning of the last name byte (the last character of the name or a space to pad to an even number of bytes). So, there are four differences between the dictionaries of fbForth and TurboForth: fbForth does not track the originating block except at LOAD time for error assistance. There are terminator bits (80h) in the first and last name bytes. The length byte is, well, one byte versus TurboForth's one word (2 bytes). Consequently, with 3 marker bits (terminator, precedence [immediate], smudge [hidden]), names can be 31 characters long. The label field points to the previous word's name field instead of the previous word's label field as with TurboForth. I was, rather, looking for a neater way to present the name field to a reader of the source code. I fear I have found it. ...lee Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 17, 2014 Author Share Posted March 17, 2014 Here's my last attempt with the slight improvement of mnemonics for the marker bits and space correction: TERMHI EQU >8000 ; terminator bit for high byte of word SMDGHI EQU >2000 ; smudge bit for high byte of word (same as space character) TERMLO EQU >0080 ; terminator bit for low byte of word *** EMPTY-BUFFERS *** DATA L10FC L10FE DATA TERMHI-SMDGHI+>0D00+' E','MP','TY','-B','UF','FE','RS'+TERMLO EMPTYB DATA DOCOL,FIRST,LIMIT,OVER,SUB,ERASE,FLUSH DATA FIRST,USE,STORE,FIRST,PREV,STORE,SEMIS Note that the space correction has the same value as the smudge bit. Using the same mnemonics as above, here is another possibility: *** EMPTY-BUFFERS *** DATA L10FC L10FE BYTE TERMLO+13 ; <--makes character count easier to read TEXT 'EMPTY-BUFFER' BYTE 'S'+TERMLO EMPTYB DATA DOCOL,FIRST,LIMIT,OVER,SUB,ERASE,FLUSH DATA FIRST,USE,STORE,FIRST,PREV,STORE,SEMIS I still think the first one is easier to read, even with the aggravating punctuation, because the second one will always have the last character of any odd-length name on the next line, unless I can figure out a way to modify the last byte during assembly. ...lee Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 17, 2014 Author Share Posted March 17, 2014 Oops! The length of the name field line with the DATA directive in my last post is 13 characters too long for assembly with the TI Editor/Assembler. Does anybody know the limit for Asm994a? @Willsy? @Tursi? @InsaneMultitasker? @matthew180? Anyone else? ...lee Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 17, 2014 Author Share Posted March 17, 2014 Oops! The length of the name field line with the DATA directive in my last post is 13 characters too long for assembly with the TI Editor/Assembler. Does anybody know the limit for Asm994a? @Willsy? @Tursi? @InsaneMultitasker? @matthew180? Anyone else? ...lee It works fine in Asm994a except that Asm994a does not like the first expression AT ALL! If I make the ' E' the first term, it works!? Though this is a puzzle I would like answered, I actually stumbled upon a better solution that does not need the leading space coupled with its awkward removal: TERMBT EQU >80 terminator bit PRECBT EQU >40 precedence (immediate) bit SMDGBT EQU >20 smudge (hidden) bit LSHFT8 EQU >100 multiplier for left shift of 8 bits (1 byte) * * *** EMPTY-BUFFERS *** DATA L10FC L10FE DATA 13+TERMBT*LSHFT8+'E','MP','TY','-B','UF','FE','RS'+TERMBT EMPTYB DATA DOCOL,FIRST,LIMIT,OVER,SUB,ERASE,FLUSH DATA FIRST,USE,STORE,FIRST,PREV,STORE,SEMIS The first expression now has the decimal character count for the name as the first entry followed by the addition of any marker bits, all of which get shifted into the left byte with "*LSHFT8" just before the addition of the first character of the name. As far as I can tell, EMPTY-BUFFERS at 13 characters is, indeed, the longest word in fbForth, so all of the high-level Forth words in the resident dictionary's ALC source file will assemble successfully with the name field on one reasonably readable line. At the very least, it makes the ALC eminently easier to maintain! ...lee 1 Quote Link to comment Share on other sites More sharing options...
Willsy Posted March 18, 2014 Share Posted March 18, 2014 I think that's the best you'll do with that dictionary format. It's more readable than before! You'll still need to use comments to separate your code, as you have above: *** EMPTY-BUFFERS *** Purely so that you can search for the bloody things using the search function in your text editor! 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 19, 2014 Author Share Posted March 19, 2014 (edited) fbForth/TI-Forth compatibility issue— I am currently puzzling over how to manage the fbForth 40/80-column and 64-column editors in cartridge space. For the most part, I plan to re-write them in ALC because I want to run them from cartridge ROM and leaving them coded in high-level Forth won't let me do that with the limited space in the dictionary bank. The reason I say "puzzling over..." is that there are a handful of words used only by the 64-column editor as fbForth is supplied; but, they are detailed in the manual, which makes them visible to users and thus available for user programming. I am inclined to recode them in pure ALC, which would make them unavailable to users and, consequently, would break existing code using them. I've never seen any TI Forth programs that use them; but, that doesn't mean they don't exist. I suspect this is a non-issue given the less than overwhelming presence of extant fbForth/TI-Forth programmers. Unless someone can disabuse me of my conclusion, I think I will recode them. The relevant words are TCHAR <--array of tiny 3x7 characters in a 4x8 matrix SMASH <--formats a line of tiny characters CLINE <--displays a line of tiny characters CLIST <--displays an entire Forth block as tiny characters @Willsy? @jacquesg? @JonnyBritish? @Vorticon? @Rod Van Orden? Anyone? ...lee Edited March 19, 2014 by Lee Stewart Quote Link to comment Share on other sites More sharing options...
Willsy Posted March 20, 2014 Share Posted March 20, 2014 (edited) Have you considered just copying pre-compiled Forth code from ROM (any bank, it doesn't matter) to RAM at HERE and then just executing that code? TurboForth V1.1 does exactly that with the word WORDS. I couldn't fit it in bank 0 (where all the Forth coded stuff lives) so I just put it in bank 1. In bank 0, I have a dictionary entry for WORDS which branches into bank 1. In bank 1, the Forth code (in bank 1) is copied into RAM at HERE and then it returns to bank 0. The remainder of the code in bank 0 just branches to the code that was just copied into RAM. HERE isn't changed, so the next code to be compiled gets compiled over it, which is no problem at all. If you subsequently execute WORDS again it gets copied to RAM again, at the new address of HERE. You can see it in action in TF V1.1. It's built in to Classic99 so: Open the debugger. Start TF and hold down the ENTER key to bypass disk boot. Now set the debug CPU window to A2EA and type WORDS. You'll see the Forth code get dumped into RAM. It sounds counter-intuitive to put Forth code into bank 1, but you can do it just fine. Recall that the compiled Forth code is just a list of addresses. We write Forth code as DATA statements: DATA drop,swap,nip,plus1 etc These are just symbolic addresses. All defined in bank 0. Nothing at all stopping you from putting them in bank 1. You just can't execute them from bank 1 (because all the Forth CFAs would have been paged out of memory). HTH Edited March 20, 2014 by Willsy 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 20, 2014 Author Share Posted March 20, 2014 Have you considered just copying pre-compiled Forth code from ROM (any bank, it doesn't matter) to RAM at HERE and then just executing that code? ... No, I hadn't thought of that. Wow! This is the kind of idea I need. Thanks.! If I use HERE, I will need to be careful that none of the words in the address list uses HERE for temporary operations! I will probably hoist fbForth's low-level system support out of low RAM and put system variables there (much less space than system support). The end of that space would make a good spot for your suggestion. It is exactly analogous to HERE in fbForth in that there is nothing permanent between HERE and the parameter stack and the return stack grows toward it from high low-RAM memory just as the parameter stack grows toward HERE in high RAM. We could call it RHERE, perhaps! BTW, you're talking about TurboForth v1.2, are you not? ...lee Quote Link to comment Share on other sites More sharing options...
Willsy Posted March 20, 2014 Share Posted March 20, 2014 Hi Lee No. TF V1.1 copies WORDS into RAM (at HERE) to execute it, but V1.2 does not (I managed to make quite a few enhancements in V1.2 and remove redundancy, so much so that I had room to put WORDS back where it really belonged). Glad you like the idea. Sounds like you've got a solution bubbling away. You could also use PAD or PAD plus some offset, though strictly speaking (at least in ANS and Forth-83, don't know about '79 and FIG) a system may not avail itself of PAD; it's reserved for user code. Nothing stopping you using PAD plus some offset though. Although your suggestion to use to place it somewhere near the end of low RAM sounds perfectly fine to me. Of course, where the execution of a word takes an unusual course of action, such as copying itself to RAM and running from there, it should be documented accordingly to alert programmers of potential pitfalls. YOur standard of documentation being what it is though, I don't anticipate a problem there! Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 20, 2014 Author Share Posted March 20, 2014 No. TF V1.1 copies WORDS into RAM (at HERE) to execute it, but V1.2 does not (I managed to make quite a few enhancements in V1.2 and remove redundancy, so much so that I had room to put WORDS back where it really belonged). Aha! Still a good idea and I will definitely use it if I have to, especially for words like VLIST (fbForth's WORDS ) where the extra time for copying is not onerous. Glad you like the idea. Sounds like you've got a solution bubbling away. You could also use PAD or PAD plus some offset, though strictly speaking (at least in ANS and Forth-83, don't know about '79 and FIG) a system may not avail itself of PAD; it's reserved for user code. fbForth/TI-Forth words in the resident dictionary use PAD for ID. , <# , # , #> , and MESSAGE and, in addition, fbForth uses PAD for BFLNAME , DEFBF , MKBFL and USEBFL . However, none of the non-high-level-Forth-based system ALC uses PAD. Of course, where the execution of a word takes an unusual course of action, such as copying itself to RAM and running from there, it should be documented accordingly to alert programmers of potential pitfalls. If I take this course of action (likely), I may tell the user how to modify RHERE to reserve a small space that the system won't clobber. It all depends on how much space actually winds up available there when I'm done. I certainly don't want to leave the return stack with too little room! Your standard of documentation being what it is though, I don't anticipate a problem there! You're too kind. Thanks. ...lee Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 20, 2014 Author Share Posted March 20, 2014 Back to what to do with the 64-column editor— I expect to code all of the 64-column editor into 1 bank without any use of high-level Forth within that bank except to get back to bank0 when done. If I become convinced that I need to retain the Forth definitions of TCHAR , SMASH , CLINE and CLIST , I will need to code stubs that will get to code in the 64-column editor bank that will return to bank0 immediately. but will not interfere with their use within the editor. I guess I could use a system variable to manage that without too much additional code. After all, I could commit an entire bank to one or both editors because I plan to use a 32KB cartridge (4 banks) or even a 64KB cartridge, if necessary! ...lee 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 22, 2014 Author Share Posted March 22, 2014 My head hurts! I think I need to devise a small bank-switching test harness for all the different ideas I'm dreaming up for handling bank-switching etc. for my cartridge version of fbForth. I am thinking of ways to manage bank-switching faster than I can test them. I'm afraid I'll forget what I want to do. In some circumstances (like the 64-column editor!), I was hoping to avoid switching to invoking high-level Forth; but, I keep running into the need to do it. I probably shouldn't try to rewrite words in ALC every time I need one—mainly because they call other words! The rewrites start to get rather complicated. I'm rambling; but, any suggestions are certainly welcome. ...lee Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 25, 2014 Author Share Posted March 25, 2014 (edited) Bank-switching code for 4 banks— Here is my first attempt at 2 ALC routines for running routines in other banks and returning to the calling bank as well as 2 routines for running the payload of high-level Forth words in other banks, "returning" to bank0 and executing the next CFA (code field address) via B *NEXT: ******************************************************************** * General bank branching routine (32KB ROM, i.e., 4 banks) for a * branch that is expected to return (not high-level Forth)--- * --put in scratchpad or low RAM * --called by * BL @BLBANK * DATA dst_addr - >6000 + bank# in left 2 bits * BLBANK DECT R ;reserve space on return stack (R14) MOV @2(LINK),*R ;push return address DECT R ;reserve space on return stack MOV @MYBANK,*R ;push return bank (leftmost 2 bits) MOV *LINK,LINK ;copy destination bank address to R11 MOV LINK,CRU ;copy it to R12 ANDI LINK,>1FFF ;mask off leftmost 3 bits to reveal address - >6000 AI LINK,>6000 ;make it a real address SRL CRU,13 ;shift bank# into bits 1-2 of R12 AI CRU,>6000 ;make it a real bank-switch address CLR *CRU ;switch to destination bank B *LINK ;branch to destination address * ******************************************************************** * General bank return routine (32KB ROM, i.e., 4 banks)--- * --put in scratchpad or low RAM * --called by * B @RTBANK * RTBANK MOV *R+,CRU ;pop return bank# from return stack to R12 SRL CRU,13 ;shift bank# into bits 1-2 of R12 AI CRU,>6000 ;make it a real bank-switch address MOV *R+,LINK ;pop return address from return stack CLR *CRU ;switch to destination bank B *LINK ;branch to return address * ******************************************************************** * High-level-Forth bank branching routine (32KB ROM, i.e., 4 banks) * for a branch that is not expected to return--- * --put in scratchpad or low RAM * --called by * BL @BLFRTH * DATA dst_addr - >6000 + bank# in left 2 bits * BLFRTH MOV *LINK,LINK ;copy destination bank address to R11 MOV LINK,CRU ;copy it to R12 ANDI LINK,>1FFF ;mask off leftmost 3 bits to reveal address - >6000 AI LINK,>6000 ;make it a real address SRL CRU,13 ;shift bank# into bits 1-2 of R12 AI CRU,>6000 ;make it a real bank-switch address CLR *CRU ;switch to destination bank B *LINK ;branch to destination address * ******************************************************************** * High-level-Forth bank "return" routine (32KB ROM, i.e., 4 banks)--- * --put in scratchpad or low RAM * --called by * B @RTNEXT * RTNEXT CLR @6006 ;switch to bank0 B *NEXT ;branch to next CFA (in R15) MYBANK in line 12 needs some explanation. It will be at the same address in all 4 banks and will have the inverted bank# in the leftmost 2 bits. The following table shows the value of MYBANK for each bank: Bank Select MYBANK ---- ------ ------ 0 >6006 >C000 1 >6004 >8000 2 >6002 >4000 3 >6000 >0000 When MYBANK is actually used to return to the calling bank, it is shifted into bits 1-2 instead of 0-1 so the result will be 6, 4, 2 or 0 instead of 3, 2, 1 or 0—well, 0 doesn't change, of course. >6000 is added before the bank switch is effected. Hopefully, there is enough information in the code above that will allow you to understand how the routines work so you can evaluate them to make suggestions for better code. The DATA directive following the BL *... is composed of the destination bank# as per MYBANK and the destination address offset from >6000 to leave bit 13 as 0. The address will be corrected before the branch is taken. This device also allows for 8 banks (64KB ROM) because bits 13-15 can all three be used without interfering with the address offset. I need to proceed very carefully so as not to disturb how fbForth's interrupt processing works—especially, if I execute the low-level system support code from ROM, which I'm hoping to do. I will be putting a few critical routines in low RAM along with all system variables, buffers and workspaces that I can't put in scratchpad RAM. ...lee Edited March 25, 2014 by Lee Stewart 1 Quote Link to comment Share on other sites More sharing options...
Willsy Posted March 25, 2014 Share Posted March 25, 2014 Looks good, with nice clean concise code. No suggestions (currently) in terms of improving the code. I'm left puzzled by the routine BLFRTH, though. In what context is it used? Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 25, 2014 Author Share Posted March 25, 2014 Looks good, with nice clean concise code. No suggestions (currently) in terms of improving the code. I'm left puzzled by the routine BLFRTH, though. In what context is it used? Thanks. BLFRTH is an elaboration of the BANK1 routine in your TurboForth that will allow high-level Forth code stubs in bank 0 to call payload code in more than one bank. Obviously, I will not need anything other than BANK1 code if I can fit all of the payload guts of all words into one bank. ...lee 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 26, 2014 Author Share Posted March 26, 2014 Which is more costly in Forth, a SWAP or using the return stack with >R ... R> ? Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 27, 2014 Author Share Posted March 27, 2014 Well—if I figured this out correctly, SWAP takes 162 clock cycles and the >R ... R> combo takes 200. Just in case anyone cares to check my calculations, the relevant ALC for the three words follows: *** SWAP *** MOV *R9,R1 MOV @2(R9),*R9 MOV R1,@2(R9) B *R15 *** >R *** DECT R14 MOV *R9+,*R14 B *R15 *** R> *** DECT R9 MOV *R14+,*R9 B *R15 That would make SWAP about 20 % faster. So, SWAP it is! At least, in cases where the two constructs are effectively equivalent. ...lee Quote Link to comment Share on other sites More sharing options...
Willsy Posted March 27, 2014 Share Posted March 27, 2014 Not too mention the fact that >R and R> *each* incur a round-trip through the inner interpreter. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 27, 2014 Author Share Posted March 27, 2014 (edited) Not to mention the fact that >R and R> *each* incur a round-trip through the inner interpreter. ...which is three additional instructions for "B *R15", albeit in 16-bit RAM. That brings up another point, making it worse than I thought, viz., the formula for instruction timing in the TMS9900 Microprocessor Data Manual does not calculate the effect on timing caused by the CPU's fetching the next 2-byte word of memory on the 8-bit bus or the same circumstance for source and destination memory accesses! All of the register accesses in this instance are 16-bit; but, the indirect memory accesses through those registers are not. In another thread on this forum, @Tursi et al. discussed how to include those timing hits in the timing calculation, but I forget where. Now, my head really hurts! ...lee Edited March 27, 2014 by Lee Stewart Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.