Jump to content
IGNORED

TIPI - Geneve 9640 to Raspberry PI interface development


9640News

Recommended Posts

16 minutes ago, jedimatt42 said:

When you send the open message, it shouldn't have trailing bytes...  the sendmsg routine should be specifying the actual length of the message which in this case is 20 bytes.. 

 

This message is received by the python code and split on the ':' the second part should just be the digits in the port. But if you are sending a padded buffer, the python int(string) function is having to deal with the extra data. 

 

So your open request should only be this part:  2201 0131 3932 2E31 3638 2E31 2E37 323A 3936 3430

 

OK, I see my error.  I had hard coded a length in the sendmsg routine for the OPEN URL call, and everything in the past had been padded with >20's that were not an issue.  Got to clean up some code.


Beery

 

  • Like 1
Link to comment
Share on other sites

This is TIPI related Matt...........  First two paragraphs are giving some background information.

 

I have seen a behavior with the Geneve I remember existing back in the 90's, and is still present today when using MyWord.  On occasion, I have run a MDOS program and I will subsequently run MyWord from EXEC and the time/date information on the bottom of the screen does not display.  I believe this is tied to an interrupt handler or some bit(s) being set on the bus, but not certain.

 

On a fresh boot of MDOS, or on a warm reboot of MDOS with the "&" key, the time/date information reappear back in MyWord.

 

On a fresh boot or warm reboot of MDOS, I have an issue detecting ANSI from the MDOS CLI embedded Telnet client.  However, when I run MyTerm for the TIPI and then return to MDOS to use the Telnet client, I have no issue with the embedded telnet client detecting ANSI from the connected system.

 

Both the embedded Telnet client and MyTerm for the TIPI are using the same ANSI driver (new installable driver/XOP for MDOS) and the same XOP code for TIPI TCP access.

 

If I remember correctly Matt, I think you indicated some unreliable ANSI detection issue with your telnet client and is why I brought it up here.

 

 

Link to comment
Share on other sites

9 hours ago, 9640News said:

This is TIPI related Matt...........  First two paragraphs are giving some background information.

 

I have seen a behavior with the Geneve I remember existing back in the 90's, and is still present today when using MyWord.  On occasion, I have run a MDOS program and I will subsequently run MyWord from EXEC and the time/date information on the bottom of the screen does not display.  I believe this is tied to an interrupt handler or some bit(s) being set on the bus, but not certain.

 

On a fresh boot of MDOS, or on a warm reboot of MDOS with the "&" key, the time/date information reappear back in MyWord.

 

On a fresh boot or warm reboot of MDOS, I have an issue detecting ANSI from the MDOS CLI embedded Telnet client.  However, when I run MyTerm for the TIPI and then return to MDOS to use the Telnet client, I have no issue with the embedded telnet client detecting ANSI from the connected system.

 

Both the embedded Telnet client and MyTerm for the TIPI are using the same ANSI driver (new installable driver/XOP for MDOS) and the same XOP code for TIPI TCP access.

 

If I remember correctly Matt, I think you indicated some unreliable ANSI detection issue with your telnet client and is why I brought it up here.

 

 

I don't know how to interpret this. All I can say is TIPI only provides 'socket' level interface. Anything protocol related like TELNET, TERMinal negotiation, and processing of the bytes received is the realm of the code we write on the TMS9900 or TMS9995 to execute. Maybe one of the programs is not closing the socket? The socket object on the python side is stateful for the life of the tipi.service (which on a 4A is reset for every visit to the TI title screen) , paired with the socket id number the client chooses.  Maybe change the socket ID that MyTerm uses, and see if that stops 'clearing' the issue with the MDOS embedded TELNET. My guess is there is cleanup code in MyTerm that is missing from TELNET. And for reliability sake, it needs to be on the entry

 

What is the 'detecting ANSI' dataflow? The TELNET layer can describe the terminal upon request, a little. But does it know that the XOP based ANSI driver is loaded? Or is the server sending codes, that get blindly forwarded through the TELNET layer to the ANSI layer? And does the ANSI layer then have a means of actually responding (to the socket) ? ( I have had very poor luck finding documentation on what BBS servers attempt to do to decide if a terminal client supports ANSI or not... ) 

 

 

Link to comment
Share on other sites

33 minutes ago, jedimatt42 said:

I don't know how to interpret this. All I can say is TIPI only provides 'socket' level interface. Anything protocol related like TELNET, TERMinal negotiation, and processing of the bytes received is the realm of the code we write on the TMS9900 or TMS9995 to execute. Maybe one of the programs is not closing the socket? The socket object on the python side is stateful for the life of the tipi.service (which on a 4A is reset for every visit to the TI title screen) , paired with the socket id number the client chooses.  Maybe change the socket ID that MyTerm uses, and see if that stops 'clearing' the issue with the MDOS embedded TELNET. My guess is there is cleanup code in MyTerm that is missing from TELNET. And for reliability sake, it needs to be on the entry

 

What is the 'detecting ANSI' dataflow? The TELNET layer can describe the terminal upon request, a little. But does it know that the XOP based ANSI driver is loaded? Or is the server sending codes, that get blindly forwarded through the TELNET layer to the ANSI layer? And does the ANSI layer then have a means of actually responding (to the socket) ? ( I have had very poor luck finding documentation on what BBS servers attempt to do to decide if a terminal client supports ANSI or not... ) 

 

Matt,

 

I have posted a portion of the communication between a MyTerm connection to the Mystic BBS.  This is where an ESC[6n (>1B,>5B,>36,>6E) command is received, and then the response is sent back.

 

Receive byte(s): 1B
Receive byte(s): 5B
Receive byte(s): 36
Receive byte(s): 6E
Send byte:       1B
Send byte:       5B
Send byte:       32
Send byte:       35
Send byte:       3B
Send byte:       30
Send byte:       38
Send byte:       30
Send byte:       52

 

The Sent bytes are defined as:

 

BYTE   >1B,>5B

DATA   0       (line number, 2 digits in ASCI)

BYTE   ';','0'  (zero, not null)

DATA   0   (column number, 2 digits in ASCI)

BYTE   'R'
 

Now, on my BBS, there is text with an initial clear screen that is sent with expectation of 80x24 screen at which time the 4 receive bytes noted above come in the sequence.  It "knows" where the cursor should be at the end of the text sequence, and I believe the response with the line number and column number should match its expected location.  At least that is my take.

 

Still working through the issue as something isn't adding up.

 

 

 

 

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

18 hours ago, 9640News said:

BYTE   >1B,>5B

DATA   0       (line number, 2 digits in ASCI)

BYTE   ';','0'  (zero, not null)

DATA   0   (column number, 2 digits in ASCI)

BYTE   'R'

FYI, (you may already know this) 6n is a request to query cursor position and 'R' reports the position.  This is not the terminal type query or detection, though if a terminal responds you can infer some level of capability. 

Link to comment
Share on other sites

  • 1 month later...

Matt,

 

I have a question before I potentially test a proof of concept idea.  I am looking at the multitasking potential of the Geneve 9640 to launch multiple running copies of a BBS program if there are more than one caller to the system.  I am going to be realistic here as I think there would be few if any individuals that would dedicate a Geneve system these days.  Still though, if it works, it gives me some ideas for other programing opportunities. 

 

I have a RS232 based Geneve MDOS BBS program that has already been written that would just take some tweaks similar to the AfterHours BBS program that gives me something running where I can also see speed impact with multiple programs running.  That is why I am using that program as the test case.

 

On inbound connections with the PI.  Lets say I have port 9640 routed to the TIPI/PI.  I am looking for the the calling client to connect to the BBS server.  I am listening and receive a message with the server byte.  I pass the server byte to the BBS program and launch it with that connection.  Now, what would happen if I have another client trying to  connect to the BBS server if I am continuing to listen?  Will it only report a new server byte when one is trying to connect?  Or, if there is no new incoming connection, will it report the previous incoming, or nothing new, or only anything that has not been binded yet?  Trying to understand what response and/or limitation I may have to manage.

 

Thanks for any feedback.

 

 

 

 

 

Link to comment
Share on other sites

when you 'bind' the server socket the well known port you bound to becomes available for incoming connections. You only have to bind once.

 

incoming 'callers' are queued up waiting for you to 'accept' them. 'accept' assigns an available ID to the incoming socket (1-254) ... so you can have 254 concurrent connections open to different callers. If accept returns 0, there was no caller queued up. If it returns 255 something bad happened to the server-socket... 

 

When you 'close' an 'accept'ed connection, the ID is put back in the pool for reuse... when 'accept' a new connection I allocate the lowest value ID that is not in use. 

 

You could have a top process that binds the server socket, and loops processing 'accept'... when an 'accept'  returns a valid ID, you start another process and give it that client socket ID to process... Each caller would be a process. It would 'open' the client ID, handle the user BBS session, and then 'close' and exit. 

 

Your multitasking needs to be aware that calls to TIPI message operations must be completed tipi_send and tipi_recv pairs. 

  • Like 2
Link to comment
Share on other sites

Thanks for the feedback.  It sounds like what I have in mind is possible.  And, I have tipi_send and tipi_recv pairs already connected via an XOP that was added in V7.30.  While in the XOP call of the TIPI library, I go in with interrupts off and return with interrupts on during all the communication calls for each handle byte.  I should be good.


Beery

  • Like 1
Link to comment
Share on other sites

Matt,

 

I am in the process of updating the embedded MDOS TIPI XOP for the server side of things and went looking at your original code on github.  In TcpFile.py, I saw this where I highlighted as it differs from the the client handle bytes code you used for the client TCP extensions.

 

    def bindServer(self, host, port?
  iface_addr = host + ':' + port
  logger.info(f'BIND, interface address: {iface_addr}')
  msg = bytearray(len(iface_addr) + 3)
  msg[0] = 0x22
  # from DSR level 3 io, we'll only do one listening port
  # it's an old computer.
  msg[1] = 0x00
  msg[2] = 0x05
  msg[3:] = bytearray(iface_addr, 'ascii')
  res = self.tisockets.processRequest(msg)
  if res == BAD:
 

raise Exception("error binding port")

 

def accept(self, devname?
  logger.info("accepting from devname: %s", devname)
  msg = bytearray(3)
  msg[0] = 0x22
  msg[1] = 0x00
  msg[2] = 0x07
  res = self.tisockets.processRequest(msg)
  if res == ACCEPT_ERR:
  logger.error("accept error, devname: %s", devname)
  self.tipi_io.send([EFILERR])
  else:
  # If there was no error, but no pending connect this just passes
  # the zero back through the read operation.
  logger.info("accept success, handle: %d, devname: %s", res[0], devname)
  self.tipi_io.send([SUCCESS])
 

self.tipi_io.send(bytearray(str(int(res[0])), 'ascii'))

 

def unbind(self, devname?
  msg = bytearray(3)
  msg[0] = 0x22
  msg[1] = 0x00
  msg[2] = 0x06
  self.tisockets.processRequest(msg)

 

Does this indicate the server byte at msg[1] can only be hex >00 for the Bind Server, Accept, and UnBind Server extensions?  I want to confirm, as if that is the case and will remain the case, then I can eliminate some code on my end.  Also, if that is the case, then the Extension-TCP Wiki content under Server Sockets need to be updated as it indicates the server-byte value can be from 0-255.

 

Beery

 

 

 

Link to comment
Share on other sites

DSR LVL3 is where you use a DSRLNK to call to something like "PI.TCP=blahblah..." as an OPEN file operation. TcpFile.py is the FILE IO wrapper around TiSocket.py which implements the TIPI messaging API for sockets. You are using the TIPI messaging interface so you don't care about "PI.TCP" file limitations.  Also, that limit is only on IDs of server sockets... which is a separate set of IDS from the client sockets that represent the individual incoming users.  

 

You only need one server socket to serve up to 254 incoming clients.  The server socket 'accept' operation will return an ID from pool of client IDs to then use... 

 

Code : https://github.com/jedimatt42/tipi/blob/main/services/TiSocket.py - comments at the top of out of date... 

Docs : https://github.com/jedimatt42/tipi/wiki/Extension-TCP - the authoritative docs, I believe are up to date.

 

There can indeed be multiple ports to listen on. Each would require a different server ID (the server-byte) in the message format.

Link to comment
Share on other sites

20 minutes ago, jedimatt42 said:

DSR LVL3 is where you use a DSRLNK to call to something like "PI.TCP=blahblah..." as an OPEN file operation. TcpFile.py is the FILE IO wrapper around TiSocket.py which implements the TIPI messaging API for sockets. You are using the TIPI messaging interface so you don't care about "PI.TCP" file limitations.  Also, that limit is only on IDs of server sockets... which is a separate set of IDS from the client sockets that represent the individual incoming users.  

 

You only need one server socket to serve up to 254 incoming clients.  The server socket 'accept' operation will return an ID from pool of client IDs to then use... 

 

Code : https://github.com/jedimatt42/tipi/blob/main/services/TiSocket.py - comments at the top of out of date... 

Docs : https://github.com/jedimatt42/tipi/wiki/Extension-TCP - the authoritative docs, I believe are up to date.

 

There can indeed be multiple ports to listen on. Each would require a different server ID (the server-byte) in the message format.

 

OK.  I see the TiSocket.py file now with the details.  Did not see it before.  I was trying to update the TIPI XOP to be flexible enough to support multiple server sockets to allow listening on different ports for the next release of MDOS.  For MDOS V7.30, nobody has requested details on the TIPI XOP so it was not an issue with updating the code to be more flexible.  I think I have everything tested with the added flexibility on the client side along with the TIPI mouse emulation as well.  Just trying to confirm everything on the server side of the TCP sockets with the XOP for multiple handles.

 

Beery

 

Link to comment
Share on other sites

Matt,

 

I have not confirmed with certainty yet as I need to add some additional debug feedback, but I am observing some behavior I did not expect.  Please let me know if see my approach to my testing has a problem, etc.

 

My Server Bind, Server Accept, and Server Unbind are all part of an XOP.

 

Step 1. On the Geneve, I do a Server Bind on "*:9640".  Verified I got a good bind.

 

Step 2. I then do a Server Accept on the server handle byte from Step 1.  I sit and wait testing for a ">00", ">FF", and then anything in between.  The Server Accept stays in a loop giving me a visual display each time through the loop with either the >00 or >FF. No display message on which is which while in that loop.

 

Step 3. On a Window telnet client, I open a connection to "192.168.1.79 9640".  

 

Step 4. On the Geneve, I get an "Accept" response with debug information on the server handle byte of ">01".

 

Step 5. On the Windows Telnet side, I get a connection. I do no further response after I got the connection.

 

Step 6. On the Geneve, I loop back to the Server Accept on the same original server handle byte looking for the next (2nd) connection.  At this point, I remain locked up in my XOP call.  As a note, anywhere in Step 1 through Step 3, I can do a CTRL-C as I am not having an issue with interrupts and MDOS has a programmed vector to do a B @ROUTINE.  In my case, that goes to a BLWP @0 routine in my test program.  So, the lockup is telling me somewhere in the SENDMSG and RCVMSG routines I am not getting released as I have interrupts off.  

 

Step 7. Even though Step 6 appears to have locked the Geneve up within the XOP code where interrupts are off, I am able to open a 2nd Telnet client on the same Windows machine when I successfully open a connection to "192.168.1.79 9640" so that portion of the PI code seems to be handling things. 

 

I am going to add some debug routines inside the Server Accept this evening, and I forgot to look at the tipi.log file last night to see what it may have reported as I had to retire for the evening.

 

Anything there sound like my approach may be at issue?  It appears I just can not do a "Server Accept" on a second simultaneous connection at the moment.  I also haven't tried to to unbind the first connection which would be another test I could do as well.

 

Beery

 

 

 

 

Link to comment
Share on other sites

Here are the contents to tipi.log .  I had to turn my system off after the lockup.

 

2022-01-19 18:35:06,667 TipiService : INFO     physical mode enabled
2022-01-19 18:35:06,673 TipiService : INFO     TIPI Ready
2022-01-19 18:38:38,535 TiSocket    : INFO     bind socket(0) *:9640
2022-01-19 18:38:38,535 TiSocket    : INFO     bind success
2022-01-19 18:38:40,696 TiSocket    : INFO     handleAccept
2022-01-19 18:38:40,696 TiSocket    : INFO     new connection handle: 1
2022-01-19 18:38:40,697 TiSocket    : INFO     accepting...
2022-01-19 18:38:40,698 TiSocket    : INFO     no connection available
2022-01-19 18:38:42,848 TiSocket    : INFO     handleAccept
2022-01-19 18:38:42,848 TiSocket    : INFO     new connection handle: 1
2022-01-19 18:38:42,848 TiSocket    : INFO     accepting...
2022-01-19 18:38:42,849 TiSocket    : INFO     accept conn: <socket.socket fd=6, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.1.79', 9640), raddr=('192.168.1.72', 54560)>
2022-01-19 18:38:42,849 TiSocket    : INFO     connection socket given handleId 1
2022-01-19 18:38:45,016 TiSocket    : INFO     handleAccept
2022-01-19 18:39:09,939 TipiService : INFO     physical mode enabled
2022-01-19 18:39:09,944 TipiService : INFO     TIPI Ready
 

And here are the contents of the daemon.log file after I forced a reboot of the PI from when I launched the Geneve program.

 

Jan 19 18:39:01 geneve1 systemd[1]: Starting Clean php session files...
Jan 19 18:39:01 geneve1 systemd[1]: phpsessionclean.service: Succeeded.
Jan 19 18:39:01 geneve1 systemd[1]: Started Clean php session files.
Jan 19 18:39:09 geneve1 systemd[1]: Stopping TI-99/4A DSR Service...
Jan 19 18:39:09 geneve1 systemd[1]: tipi.service: Main process exited, code=killed, status=15/TERM
Jan 19 18:39:09 geneve1 systemd[1]: tipi.service: Succeeded.
Jan 19 18:39:09 geneve1 systemd[1]: Stopped TI-99/4A DSR Service.
Jan 19 18:39:09 geneve1 systemd[1]: Started TI-99/4A DSR Service.
Jan 19 18:39:09 geneve1 tipi.sh[740]: checking for operation mode...
Jan 19 18:39:29 geneve1 systemd[1]: Created slice User Slice of UID 999.
Jan 19 18:39:29 geneve1 systemd[1]: Starting User Runtime Directory /run/user/999...
Jan 19 18:39:29 geneve1 systemd[1]: Started User Runtime Directory /run/user/999.
Jan 19 18:39:29 geneve1 systemd[1]: Starting User Manager for UID 999...
Jan 19 18:39:29 geneve1 systemd[752]: Reached target Timers.
Jan 19 18:39:29 geneve1 systemd[752]: Listening on GnuPG cryptographic agent and passphrase cache (access for web browsers).
Jan 19 18:39:29 geneve1 systemd[752]: Listening on GnuPG cryptographic agent (ssh-agent emulation).
Jan 19 18:39:29 geneve1 systemd[752]: Listening on GnuPG cryptographic agent and passphrase cache (restricted).
Jan 19 18:39:29 geneve1 systemd[752]: Listening on GnuPG network certificate management daemon.
Jan 19 18:39:29 geneve1 systemd[752]: Reached target Paths.
Jan 19 18:39:29 geneve1 systemd[752]: Listening on GnuPG cryptographic agent and passphrase cache.
Jan 19 18:39:29 geneve1 systemd[752]: Reached target Sockets.
Jan 19 18:39:29 geneve1 systemd[752]: Reached target Basic System.
Jan 19 18:39:29 geneve1 systemd[752]: Reached target Default.
Jan 19 18:39:29 geneve1 systemd[752]: Startup finished in 138ms.
Jan 19 18:39:29 geneve1 systemd[1]: Started User Manager for UID 999.
Jan 19 18:39:29 geneve1 systemd[1]: Started Session 2 of user tipi.
 

 

Link to comment
Share on other sites

OK, I added some debug routines for the BIND, ACCEPT, and UNBIND codes that sends text out to a RS232 port while I am in the XOP code.  What I am seeing is that the Server Accept RCVMSG routine is where the hangup is.

 

The RCVMSG routine has worked everywhere else.  I have included the source below as a reference to what I am using:

 

*  TIPI RECVMSG Code lifted from DSR

TSRSET   EQU  >F100
TSRB      EQU  >0600
TSWB     EQU  >0200

TCOUT    EQU  >5FFD
TDOUT    EQU  >5FFF
RDIN      EQU  >5FFB
RCIN      EQU  >5FF9


RECVMSG

       LI   R2,TSRSET
       MOVB R2,@TCOUT


RM44   CB   @RCIN,R2
       JNE  RM44
       LI   R2,TSRB

       MOVB R2,@TCOUT
       JMP  RM2

*                          Below mod used for opening a connection
*                          to avoid a long timeout issue
RECVMSG1

       SETO R5               InsaneMultitasker Timer routine
       LI   R6,3               InsaneMultitasker Timer routine

       LI   R2,TSRSET
       MOVB R2,@TCOUT

REST11 DEC  R5           InsaneMultitasker Timer routine
       JNE  KEEP23          InsaneMultitasker Timer routine
       DEC  R6                InsaneMultitasker Timer routine
       JEQ  EXPIRED        InsaneMultitasker Timer routine

KEEP23
RM1    CB   @RCIN,R2
       JNE  REST11          InsaneMultitasker Timer routine
       LI   R2,TSRB

       MOVB R2,@TCOUT

RM2    MOVB @RCIN,R3
       CB   R2,R3
       JNE  RM2

       AI   R2,>0100
       ANDI R2,>0100
       ORI  R2,TSRB

       MOVB @RDIN,R4
       SWPB R4
       MOVB R2,@TCOUT

RM3    MOVB @RCIN,R3
       CB   R2,R3
       JNE  RM3

       AI   R2,>0100
       ANDI R2,>0100

       ORI  R2,TSRB
       MOVB @RDIN,R4
       SWPB R4
       CLR  R0
       CI   R4,>0000

       JEQ  RRT

RNEXT  MOVB R2,@TCOUT

RM4    MOVB @RCIN,R3
       CB   R2,R3
       JNE  RM4

       AI   R2,>0100
       ANDI R2,>0100
       ORI  R2,TSRB

       MOVB @RDIN,R3
       MOVB R3,*R1+

       INC  R0
       C    R0,R4
       JNE  RNEXT
RRT    RT


EXPIRED
       CLR  @TDOUT       Clear TIPI data output
       CLR  @TCOUT       Clear control output

       SBO  1                  trigger reset signal to RPi
       LI   R1,>0200
RESTA9 DEC  R1
       JNE  RESTA9
       SBZ  1                   Turn off the reset signal to RPi service can finish
       RT

Link to comment
Share on other sites

This update looks to fix the issue.  I was able to open 4 telnet connections back to the Server Accept.  Did not try anything more, but that was good.  I was also able to Server Bind a second port after the first connection and get a Server Accept on a second port.

 

Thanks for the fix.

 

Beery

 

  • Like 3
Link to comment
Share on other sites

@jedimatt42,

 

OK, I do 4 server binds for *:9640, *:9641, *:9642, *:9643.

 

I use a telnet client to connect to 9640.  When I see a Server Accept connection, a separate program launches that is passed the handle byte.  I get information passed back to the telnet client from both the Server manager program I am running as well as the launched program with a Client Socket Write with the respective handle byte.

 

OK so far.

 

Now, I use a 2nd copy of a telnet client to connect to 9641.  The Telnet client connects as it does not timeout.  My debugging information says I get a connect for the next handle byte up.  However, if I do a Client Socket write to this second telnet client, the string goes back to the telnet client that connected on port 9640, not 9641.  

 

The issue repeats itself when port 9642 and 9643 on the Server manager connects.  All writes go back to port 9640 and not to the connecting port.

 

Beery

 

  • Like 2
Link to comment
Share on other sites

https://github.com/jedimatt42/fcmd/blob/jm42/server_socket_test/example/gcc/server/main.c

 

Logic begins in the fc_main function...  This is an example that works for me to bind the server socket.  then in a loop, if all my (arbitrarily) 10 slots are not full, attempt to accept a client. After an accept attempt, it iterates over the array I maintain of accepted clients, and attempts to handle some input/output from each. 

 

when handling a single client's interaction, the server reads from the socket with the client id... echos that to the screen, and then sends text to the client stating the client_id (so I know the correct message is going to the correct clients)... 

 

If I get no input from the client, I also send a NUL byte message to the client socket, to hopefully, eventually trigger disconnect detection. Which seems to work from watching logs.

 

I've tested this with as many as 3 simultaneous telnet sessions so far. All seems to work as expected, and illustrates that only 1 bind is needed. ( You don't want your 'callers' to have to hunt through a set of ports anyway )

 

I'll try the multi port bind situation tomorrow (today, but after sleep and work) 

 

 

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