Jump to content
IGNORED

RXB - Rich Extended Basic


Bones-69

Recommended Posts

I don't believe you can do this directly with the EA5 loader, but I think it can be done this way:

1 - write an assembly subroutine that first copies VDP memory to a disk file. The brute force method would be to copy the entire VDP, the finesse method would just copy the areas actually used by XB. Then it should copy high memory. As before, you can brute force it or finesse it. Finally copy low memory and the scratchpad to another file. Again, brute force or finesse - your choice. The files should probably have sequential names like in EA5 programs - i.e. GAME, GAMF, etc. Also, you would have to record the workspace and program counter.

2 - load the XB program, then use CALL INIT and CALL LOAD to load the assembly routine to low memory. Add CALL LINK("PROGNAME") to your XB program at the line where you would like it to be saved. This could be at the beginning after the prescan or even better, after all the characters have been defined and the screen drawn. Run the program to save all the memory locations.

3 - write a loader that runs as an EA5 program. This should read the files created above and load them into the proper memory locations. Set up the workspace and program counter and now you are exactly where you were when you interrupted the program to save the files. Return to XB and you will resume running the program at the point where it was saved.

 

Of course, all this assumes that you still have the XB cartridge in the slot.

 

I was hoping to do this without any user input in XB to make it work. I understand saving all the memory areas used by the XB program and loading then via assembly into the proper areas. I'm interested in storing the program in the banked c>6000 area of the ubergrom cart. I would then construct a cartridge header (or include it in an already constructed menu) to start an assembly program to move the code into the proper places. Naturally a version of Extended Basic would be available at Grom base >9800. What I don't understand, and believe can be done, is to then start the program running by assembly code without any user intervention. I believe Bruce Harrison's XB compiler fed commands to the XB interpreter for the commands that couldn't be converted to assembly. It seems to me that it should be possible to load the XB program in the proper areas and feed a RUN <enter> to the XB interpreter via assembly and make my idea work. I just need the last piece of the puzzle. :)

 

Gazoo

Link to comment
Share on other sites

 

I was hoping to do this without any user input in XB to make it work. I understand saving all the memory areas used by the XB program and loading then via assembly into the proper areas. I'm interested in storing the program in the banked c>6000 area of the ubergrom cart. I would then construct a cartridge header (or include it in an already constructed menu) to start an assembly program to move the code into the proper places. Naturally a version of Extended Basic would be available at Grom base >9800. What I don't understand, and believe can be done, is to then start the program running by assembly code without any user intervention. I believe Bruce Harrison's XB compiler fed commands to the XB interpreter for the commands that couldn't be converted to assembly. It seems to me that it should be possible to load the XB program in the proper areas and feed a RUN <enter> to the XB interpreter via assembly and make my idea work. I just need the last piece of the puzzle. :)

 

Gazoo

 

I would look at the MENU / BOOT source code. You should be able to mimic loading the program into memory and then use the MENU loader process to start things up. If I remember correctly, similar to your comments about Harrison's compiler, the MENU loader feeds some commands via interrupt into the XB interpreter. But it's been a long time since I delved into that....

Link to comment
Share on other sites

 

I was hoping to do this without any user input in XB to make it work.

Sorry, I guess I wasn't clear on what I was thinking. When you use CALL LINK to start an assembly subroutine in XB, you can return to XB with (if I am remembering correctly) LWPI >83E0 and then B @>006A. So if you write the first assembly program I outlined above (make sure this is generic - i.e. so you can save any XB program), then use call link to enter the subroutine that copies the memory locations. Put this in the program after the character definitions and screen image has been defined. Now you could return from the A/L sub to XB as described above and continue running the XB program. Your custom loader (and again, make sure this is as generic as possible) simply uses the files created to restore all the pertinent memory locations including the scratchpad and setting the registers to the XB values. The loader then should be able to enter the XB program with LWPI >83E0 and B @>006A and you should be back in the XB program exactly where you were when you made the memory image with the prescan done and the characters all defined. (If your XB program uses a lot of A/L subroutines you would have to be careful to avoid disrupting them with the loader and saver program.)

Link to comment
Share on other sites

Sorry, I guess I wasn't clear on what I was thinking. When you use CALL LINK to start an assembly subroutine in XB, you can return to XB with (if I am remembering correctly) LWPI >83E0 and then B @>006A. So if you write the first assembly program I outlined above (make sure this is generic - i.e. so you can save any XB program), then use call link to enter the subroutine that copies the memory locations. Put this in the program after the character definitions and screen image has been defined. Now you could return from the A/L sub to XB as described above and continue running the XB program. Your custom loader (and again, make sure this is as generic as possible) simply uses the files created to restore all the pertinent memory locations including the scratchpad and setting the registers to the XB values. The loader then should be able to enter the XB program with LWPI >83E0 and B @>006A and you should be back in the XB program exactly where you were when you made the memory image with the prescan done and the characters all defined. (If your XB program uses a lot of A/L subroutines you would have to be careful to avoid disrupting them with the loader and saver program.)

This is exactly why I created the RXB command CALL EXECUTE(cpu-address) to replace CALL LINK

The main issue with CALL LINK is it is much harder to use and requires more expertise to use then CALL EXECUTE as unlike CALL LINK is built upon LWPI >CPU ADDRESS

So works the same as:

 

LWPI >cpu-address

 

your program here

 

RTWP

 

You get your own register set and nothing in the XB program scratch pad or pointers are changed.

 

A bonus is that CALL EXECUTE if 5 times faster then CALL LINK

Link to comment
Share on other sites

Sorry, I guess I wasn't clear on what I was thinking. When you use CALL LINK to start an assembly subroutine in XB, you can return to XB with (if I am remembering correctly) LWPI >83E0 and then B @>006A. So if you write the first assembly program I outlined above (make sure this is generic - i.e. so you can save any XB program), then use call link to enter the subroutine that copies the memory locations. Put this in the program after the character definitions and screen image has been defined. Now you could return from the A/L sub to XB as described above and continue running the XB program. Your custom loader (and again, make sure this is as generic as possible) simply uses the files created to restore all the pertinent memory locations including the scratchpad and setting the registers to the XB values. The loader then should be able to enter the XB program with LWPI >83E0 and B @>006A and you should be back in the XB program exactly where you were when you made the memory image with the prescan done and the characters all defined. (If your XB program uses a lot of A/L subroutines you would have to be careful to avoid disrupting them with the loader and saver program.)

 

That sounds simple enough, thanks for the assistance. I'll give it a try this weekend.

 

Gazoo

Link to comment
Share on other sites

The memory image you create will have a CALL LINK that points to the A/L subroutine that created the memory image. Not a problem if you start the program as described above, but if you want to run the XB program from the beginning,it will try to run the subroutine that was used to create the memory image. So the loader should doctor that A/L sub to do nothing and simply return to XB.

Link to comment
Share on other sites

Well the project to make a XB RUN string command is dead:

 

RUN A$

or

100 X$="DSK.DISKNAME.FILENAME" :: RUN X$

 

The Assembly ROMs in XB will not allow a RUN STRING$ to exist as it considers it a Syntax Error.

Any attempt to bypass the Assembly results in further complications in running programs as I get ERROR BAD ARGUMENT OR SYNTAX ERROR or STRING NUMBER MISMATCH.

You can turn off the PRESCAN but still when the program runs it refuses to find the string value location as it pushes a error on the stack in Assembly ROMs.

 

In order to do many features I want in XB I need to rewrite the XB ROMs.

That way I could add another token >C6 for STRING VARIABLE and this would allow a String Variable to be a Line Number or a Pathname or Device.

Link to comment
Share on other sites

Sorry, I guess I wasn't clear on what I was thinking. When you use CALL LINK to start an assembly subroutine in XB, you can return to XB with (if I am remembering correctly) LWPI >83E0 and then B @>006A. So if you write the first assembly program I outlined above (make sure this is generic - i.e. so you can save any XB program), then use call link to enter the subroutine that copies the memory locations. Put this in the program after the character definitions and screen image has been defined. Now you could return from the A/L sub to XB as described above and continue running the XB program. Your custom loader (and again, make sure this is as generic as possible) simply uses the files created to restore all the pertinent memory locations including the scratchpad and setting the registers to the XB values. The loader then should be able to enter the XB program with LWPI >83E0 and B @>006A and you should be back in the XB program exactly where you were when you made the memory image with the prescan done and the characters all defined. (If your XB program uses a lot of A/L subroutines you would have to be careful to avoid disrupting them with the loader and saver program.)

 

I've got the solution, and it is very close to what you suggested, thank you Sir. The pertinent memory areas need to be saved after loading, but BEFORE running the program. This can be accomplished by a memory dump in Classic99. The EA5 program must then restore those areas and GPLLNK into XB at address >64DA.

That's the only way I could get it to work. The B @>006A approach always causes an error on the line that execution begins at, regardless of where it is in the program.

 

Gazoo

Link to comment
Share on other sites

 

I've got the solution, and it is very close to what you suggested, thank you Sir. The pertinent memory areas need to be saved after loading, but BEFORE running the program. This can be accomplished by a memory dump in Classic99. The EA5 program must then restore those areas and GPLLNK into XB at address >64DA.

That's the only way I could get it to work. The B @>006A approach always causes an error on the line that execution begins at, regardless of where it is in the program.

 

Gazoo

What a messed up way to fix the problem using >64DA in XB.

[0821] 64DA D5,30,32 SZRUN0 DCEQ @ENLN,@STLN       Refuse without program
[0822] 64DD 64,EF           BS   ILLST
[0823] 64DF BD,A3,72        DST  @ENLN,V@START     Defualt to beginning
       64E2 32
[0824] 64E3 A7,A3,72        DSUB 3,V@START         Offset into the table
       64E6 00,03
[0825] 64E8 45,02           BR   SZRUN1            Merge in below
[0826]               *    Jump always
[0827] 64EA D5,30,32 SZRUN2 DCEQ @ENLN,@STLN       Refuse without program
[0828] 64ED 44,F9           BR   G64F9
[0829] 64EF 0F,83    ILLST  XML  SCROLL            Scroll the screen for message
[0830] 64F1 86,44           CLR  @PRGFLG           Prevent line # printing
[0831] 64F3 06,6A,82 WRNNPP CALL G6A82
[0832] 64F6 1D              BYTE 29                * NO PROGRAM PRESENT
[0833] 64F7 43,DD           BR   TOPL15
[0834]               *    Condition can never be set since line 0 is prohibited
[0835] 64F9 0F,7E    G64F9  XML  SPEED
[0836] 64FB 03              BYTE SEETWO          * Find the line in the program
[0837] 64FC 49,8C           BR   ERRLNF            * LINE NOT FOUND
[0838] 64FE BD,A3,72        DST  @EXTRAM,V@START   Program run starts here
       6501 2E
[0839]               * GKXB RUN code for color change, routine exits to G6504.
[0840] 6502 57,74    SZRUN1 BR   RUNPAT            Change colors.
[0841] 6504 06,80,12 G6504  CALL CLSALL            Close any open files
[0842] 6507 92,44           DEC  @PRGFLG           Put it back in execution
[0843] 6509 BC,80,89        ST   @RAMTOP+1,@RAMFLG Set/reset RAMFLG flag -- when
       650C 80,85
[0844] 650E 87,A3,86        DCLR V@SEXTRM           in program mode & ERAM exist
[0845] 6511 87,A3,8A        DCLR V@ERRLN           Disallow CONTINUE after RUN
[0846] 6514 06,68,DC        CALL KILSYM            Reset ERR handling to defualt
[0847] 6517 BE,73,88        ST   RSTK,@SUBSTK      Set the stack empty
[0848]               * RXB PATCH CODE ADDED LABEL SZRUN4 ************ 
[0849] 651A 05,6A,70 SZRUN4 B    G6A70
[0850] 651D 05,D0,0D EDTZ05 B    EDTZ00
[0851]               **************************** CONTINUE *********************

My RXB CALL EXECUTE(cpu-address) avoids all that and can be used like a CALL LINK with none of the hassles.

 

1 hidden loader here with EA5 program in XB program

10 CALL EXECUTE(CPU ADDRESS)

 

As the RXB EXECUTE uses BLWP you can even use the entire FAST RAM if you just do a move the 256 bytes to a safe location,

then before you end the EA5 program move the 256 bytes back and do a RTWP

 

How freaking simple can that get?

Edited by RXB
Link to comment
Share on other sites

What a messed up way to fix the problem using >64DA in XB.

[0821] 64DA D5,30,32 SZRUN0 DCEQ @ENLN,@STLN       Refuse without program
[0822] 64DD 64,EF           BS   ILLST
[0823] 64DF BD,A3,72        DST  @ENLN,V@START     Defualt to beginning
       64E2 32
[0824] 64E3 A7,A3,72        DSUB 3,V@START         Offset into the table
       64E6 00,03
[0825] 64E8 45,02           BR   SZRUN1            Merge in below
[0826]               *    Jump always
[0827] 64EA D5,30,32 SZRUN2 DCEQ @ENLN,@STLN       Refuse without program
[0828] 64ED 44,F9           BR   G64F9
[0829] 64EF 0F,83    ILLST  XML  SCROLL            Scroll the screen for message
[0830] 64F1 86,44           CLR  @PRGFLG           Prevent line # printing
[0831] 64F3 06,6A,82 WRNNPP CALL G6A82
[0832] 64F6 1D              BYTE 29                * NO PROGRAM PRESENT
[0833] 64F7 43,DD           BR   TOPL15
[0834]               *    Condition can never be set since line 0 is prohibited
[0835] 64F9 0F,7E    G64F9  XML  SPEED
[0836] 64FB 03              BYTE SEETWO          * Find the line in the program
[0837] 64FC 49,8C           BR   ERRLNF            * LINE NOT FOUND
[0838] 64FE BD,A3,72        DST  @EXTRAM,V@START   Program run starts here
       6501 2E
[0839]               * GKXB RUN code for color change, routine exits to G6504.
[0840] 6502 57,74    SZRUN1 BR   RUNPAT            Change colors.
[0841] 6504 06,80,12 G6504  CALL CLSALL            Close any open files
[0842] 6507 92,44           DEC  @PRGFLG           Put it back in execution
[0843] 6509 BC,80,89        ST   @RAMTOP+1,@RAMFLG Set/reset RAMFLG flag -- when
       650C 80,85
[0844] 650E 87,A3,86        DCLR V@SEXTRM           in program mode & ERAM exist
[0845] 6511 87,A3,8A        DCLR V@ERRLN           Disallow CONTINUE after RUN
[0846] 6514 06,68,DC        CALL KILSYM            Reset ERR handling to defualt
[0847] 6517 BE,73,88        ST   RSTK,@SUBSTK      Set the stack empty
[0848]               * RXB PATCH CODE ADDED LABEL SZRUN4 ************ 
[0849] 651A 05,6A,70 SZRUN4 B    G6A70
[0850] 651D 05,D0,0D EDTZ05 B    EDTZ00
[0851]               **************************** CONTINUE *********************

My RXB CALL EXECUTE(cpu-address) avoids all that and can be used like a CALL LINK with none of the hassles.

 

1 hidden loader here with EA5 program in XB program

10 CALL EXECUTE(CPU ADDRESS)

 

As the RXB EXECUTE uses BLWP you can even use the entire FAST RAM if you just do a move the 256 bytes to a safe location,

then before you end the EA5 program move the 256 bytes back and do a RTWP

 

How freaking simple can that get?

 

Rich, your response has absolutely nothing to do with what I'm doing.

 

I'm executing a memory image (EA5) program from within a ROM. It transfers 8k of code from c>6000 to c>E000. The code at c>E000 then creates an Extended Basic environment in VDP and scratchpad, then it executes Extended Basic code that it loaded at the top of high memory.

 

Please explain how your comments relate to that.

 

And exactly what problem are you referring to?

 

Gazoo

Link to comment
Share on other sites

  • 3 weeks later...

Well I need to disassemble the XB ROMs at this point and I wish someone I had sent all those copies to would turn up with one.

 

If I want to and more commands to XB then this is a must as I have reached the limit of what I can do with CALL routines.

 

And then I could fix some issues in the interpreter at the ROM level.

Link to comment
Share on other sites

Just to let you know that a update on REA 2014 is going jump from a 32 column version like normal Editor Assembler to completly 40 Column version.

 

REA 2014 will look pretty much the same but will be 40 column all the time so no graphic change takes place when running the Editor or Assembler.

 

The bonus will be Catalog in REA will have more screen space of 8 characters and when running the Assembler you have bigger borders on both sides of screen.

 

Also to view a DV80 or DF80 file will look better in 40 columns.

  • Like 1
Link to comment
Share on other sites

Any chance for 80 columns at some point in the future?

Yea that is partly why I am working on it. Need to also disassembly or find the source code for the XB ROMs so I can do that too.

 

Without the rewrite of the XB ROMs doubt if we can get a 80 column version done. Access to a 80 Columns requires some changes to ROMs.

  • Like 1
Link to comment
Share on other sites

Please can you guys try out the new RND in XB I posted a easy to use version in Classic99.

 

I tested it out tonight. Here is the program that I used, which was dashed off just for this purpose. It is similar to the kaleidoscope program in your video.

5 CALL CLEAR

10 R=INT(RND*12)+1 :: C=INT(RND*16)+1

20 CALL HCHAR(R,C,42):: CALL HCHAR(25-R,33-C,42):: CALL HCHAR(R,33-C,42):: CALL HCHAR(25-R,C,42):: N=N+1 :: GOTO 10

I ran this for one minute, broke the program and printed N. Here's the results:

TIXB: original RND gave 258; fast RND gave 467

RXB: original RND gave 256; fast RND gave 457

So for that simple test program the fast RND made the program nearly 80% faster! It does seem that RXB may be a bit slower by 1 or 2 percent, probably due to the additional CALLs, but that doesn't seem like too big a deal to me.

 

You should remake the video running kaleidoscope, but this time run the program in the same version of RXB with the only difference being the use of fast or slow RND. This way, instead of comparing two different languages (XB and TI BASIC), you will have a real comparison on a real program (not my test above) with the only difference being the speed of the RND. There are more program lines and so I would guess the speed increase will be less, maybe 30%, but even so that is quite significant! I look forward to seeing your results.

Edited by senior_falcon
Link to comment
Share on other sites

Have you tried this:

5 CALL CLEAR
10 R=INT(RND*12)+1 :: C=INT(RND*16)+1
20 CALL HCHAR(R,C,42,1,25-R,33-C,42,1,R,33-C,42,1,25-R,C,42):: N=N+1 :: GOTO 10
50 ! Seems like a waste of space when using RXB.
60 CALL HCHAR(R,C,42):: CALL HCHAR(25-R,33-C,42):: CALL HCHAR(R,33-C,42):: CALL HCHAR(25-R,C,42):: N=N+1 :: GOTO 10

Just wondering how much slower that would run as RXB allows you to take out all those repetitions of :: and CALL HCHAR plus the () repeats out.

I ran the program:

5 CALL CLEAR
10 R=INT(RND*12)+1 :: C=INT(RND*16)+1
20 CALL HCHAR(R,C,42,1,25-R,33-C,42,1,R,33-C,42,1,25-R,C,42):: N=N+1 :: GOTO 10

I got 469 with RXB 2014 maybe it is my MacPro Tower?

Link to comment
Share on other sites

Well this test ran for over 2 hours and I did 4 runs to see similar results. Do not see the point of taking the same RND function and testing exactly the same thing in each version.

The test results are RXB 2014 smokes RXB 2012 and TI XB and TI Basic. I changed each program to take advantage of each Cart used.

 

 

Results:

51285 RXB 2014

27890 TI XB

27747 RXB 2012

23971 TI Basic

  • Like 2
Link to comment
Share on other sites

I have to admit to being stumped. You can think whatever you like, but why do you insist on calling it a RND test when you have changed much more than just the random number generator for each version? Clearly the idea of a controlled experiment is not something you understand.

Well let's see....

The RND GPL code is exactly the same in TI BASIC as RXB 2014 so testing only RND this way:

100 R=RND 
110 C=C+1
120 GOTO 100

That would be the TI Basic way to do it, but XB and RXB use COLONs and why are you using XB if you are going to use TI Basic rules.

But I digress:

100 R=RND :: C=C+1 :: GOTO 100 

This would be XB or RXB. The issues is like taking the wheels off your RACE CAR and putting TRACTOR TIRES on it to see if it is a tractor.

Or in this case exactly like TI BASIC as you are after all hamstringing XB and RXB to be exactly like TI BASIC does a double slow effect on XB and RXB.

 

So let us test this TI BASIC method like listed above where each line has a line number ran for 2 hours and 50 minutes:

588477 TI Basic equals per minute 3677

520085 RXB 2014 equals per minute 3251

139081 RXB 2012 equals per minute 869

138693 Extended Basic equals per minute 866

 

 

After almost 3 hours RXB 2014 is not exactly slow compared to TI Basic and shows a remarkable improvement over both XB and RXB 2012 for performance.

Which is the entire point of me doing this, not to show how great TI Basic is for doing just one single thing well and ignoring everything else it does badly.

 

So next let us test the second method but of course TI Basic has to remain with a line number per each command unlike XB or RXB that can use colons on a single line.

So 30 minutes on this test gets:

103826 TI Basic equals per minute 3460

95374 RXB 2014 equals per minute 3179

28824 RXB 2012 equals per minute 960

28745 XB equals per minute 958

 

And again RXB 2014 does a very good job over RXB 2012 or XB which is the entire point of doing this.

RXB 2014 RND is almost 3 times faster then RXB 2012 or XB so this will be a upgrade.

Edited by RXB
Link to comment
Share on other sites

That would be the TI Basic way to do it, but XB and RXB use COLONs and why are you using XB if you are going to use TI Basic rules.

But I digress:

 

Because, if you use colons rather than line numbers your results will be skewed due to the performance advantage of using colons! Do you really not get that? Can you not see it? Your two test programs must be identical so that you can measure the performance advantage of RND *only* and not other performance increasing measures such as multi-statement lines!

 

Come on, it's not exactly a difficult concept to understand! :?

  • Like 2
Link to comment
Share on other sites

But I could give a flying crap about TI Basic performance as this is XB and RXB, not a TI Basic selling point. thread.

Fine, I showed the results and tested the same exact program above and TI Basic using exactly what was requested and the performance of RXB 2014 over XB and RXB 2012 was the point.

 

The fact that TI Basic is faster at doing this is of really little value after all. Do I have to list all the things TI Basic can not possibly do?

 

And the GPL Code is exactly the same except for one GPL command to return to XB instead to TI Basic.

Link to comment
Share on other sites

Since we've been comparing apples to oranges regarding what you can and cannot do in the various Basics, fbForth 2.0 can run the program in post #416 8 times faster than RXB or TIXB using floating point RND (same as all of the Basics must do with 4 calls to integer RND) and 22 times faster with integer RND. I think these may well be close to ALC speeds.

 

...lee

Edited by Lee Stewart
  • Like 3
Link to comment
Share on other sites

But I could give a flying crap about TI Basic performance as this is XB and RXB, not a TI Basic selling point. thread.

Fine, I showed the results and tested the same exact program above and TI Basic using exactly what was requested and the performance of RXB 2014 over XB and RXB 2012 was the point.

 

The fact that TI Basic is faster at doing this is of really little value after all. Do I have to list all the things TI Basic can not possibly do?

 

And the GPL Code is exactly the same except for one GPL command to return to XB instead to TI Basic.

You are the one who initially made the comparison with TI BASIC:

Post #352: "Now to test against TI BASIC."

Post #357: A video comparing TI BASIC with RXB and TI XB

Post #387: A video comparing TI BASIC with RXB.

In both videos you maintain that RXB "smokes" TI BASIC because the random number generator is faster. How can this be? If I understand you correctly, your new random number generator is the same as the TI BASIC random number generator. In post #388 I outlined a way to test how long each statement takes to execute and showed that it is the HCHARs that make BASIC so slow. I also showed that the TI BASIC random number generator is almost 6 times faster than the XB random number generator. Since you are using the same code an RND in the new RXB should be almost as fast and at least 5x faster than the former XB RND.

 

OK fine, so the new RND is at least 5x faster. How much of a speed increase does that translate to in the real world. As you point out, one does not normally write a program that just loops and generates RNDs. I wrote the program in post #416 to get an idea and was intrigued to find that the new RXB ran it almost 80% faster compared to standard XB. But that is still not a real program. Kaleidoscope in your original video is a real program. I have it on a floppy somewhere, but you have it available more easily. I was just wondering how much difference the new RND would make running in RXB compared to running exactly the same program in standard XB, and speculated that it might be around 30% faster. That is a substantial increase in speed and if you run that test as described, then, and only then, can you say that the speed increase is due to the new RND and nothing else.

  • Like 2
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...