Jump to content
IGNORED

GET and PUT source (Compute magazine) (software sprites for ATARI BASIC)


JamesD

Recommended Posts

Compute magazine publish source to GET and PUT functions in assembly language.

The article was in issue 91, December 1987

The original source hid part of the assembly as just bytes, but I hand disassembled that part of the code and included it.

It's bound to have errors in the disassembly and the branches need labels, but I thought I'd pass on what I had in case someone has a use for it or wants to finish what I started. See the article for full details.

FYI, some instructions start in one BYTE statement and finish in the next. Tabs may have gotten eaten by the look of the code.

;Compute Issue 91 Dec 87
XLSB = 214
XMSB = 215
YLSB = 216
YMSB = 218
LENGTH = 219
BYTESLSB = 220
BYTESMSB = 221
IMAGELSB = 222
IMAGEMSB = 223
CMD = 224
*= $FFFF ;Place your program's beginning assembling address here
JMP BEGIN
GET LDA #0
JMP GP
PUT LDA #1
GP STA 227
.BYTE 169,0,133,212,133,213,1
LDA #0
STA 212
STA 213

.BYTE 214,133,225,165,89,101,215,133,226,162
OR (XLSB,X)
STA 225
LDA #89
ADC XMSB
STA 226
LDX #0

.BYTE 0,228,216,240,32,165,225,24,101,220
CPX YLSB
BEQ #32
LDA 225
CLC
ADC BYTESLSB

.BYTE 133,225,165,226,101,221,133,226,165,215
STA 225
LDA 226
ADC BYTESMSB
STA 226
LDA XMSB

.BYTE 56,233,1,133,216,165,217,233,0,133
SEC
SBC #1
STA YLSB
LDA 217
SBC #0
.BYTE 217,169,0,201,0,240,220,228,217,240
STA 217
LDA #0
CMP #0
BEQ #220
CPX 217

.BYTE 6,169,0,201,0,240,214,162,1,160
BEQ #6
LDA #0
CMP #0
BEQ #214
LDX #1

.BYTE 0,196,218,240,81,165,227,201,1,208
LDY #0
CPY YMSB
BEQ #81
LDA 227
CMP #1

.BYTE 55,165,224,201,1,208,22,177,222,201
BNE #55
LDA CMD
CMP #1
BNE #22
LDA (IMAGELSB),Y
.BYTE 0,240,10,177,225,201,0,240,4,169
CMP #0
BEQ #10
LDA (225),Y
CMP #0
BEQ #4
.BYTE 1,133,212,177,222,201,0,240,20,165
LDA #1
STA 212
LDA (IMAGELSB),Y
CMP #0
BEQ #20

.BYTE 224,201,1,240,10,177,225,201,0,240
LDA CMD
CMP #1
BEQ #10
LDA (225),Y
CMP #0
.BYTE 4,169,1,133,212,177,222,145,225,200
BEQ #4
LDA #1
STA 212
LDA IMAGELSB
STA (225),Y
INY

.BYTE 169,0,201,0,240,191,177,225,201,0
LDA #0
CMP #0
BEQ #191
LDA 225
.BYTE 240,6,169,1,133,212,177,225,145,222
BEQ #6
LDA #1
STA 212
LDA 225
STA (222),Y

.BYTE 169,0,201,0,240,229,228,219,240,35
LDA #0
CMP #0
BEQ #229
CPX 219
BEQ #35

.BYTE 160,0,165,222,24,101,218,133,222,165
LDY #0
LDA IMAGELSB
CLC
ADC YMSB
STA IMAGELSB
LDA IMAGEMSB

.BYTE 223,105,0,133,223,165,225,24,101,220
ADC #0  ;add the carry bit
STA IMAGEMSB
LDA 225
CLC
ADC BYTESLSB

.BYTE 133,225,165,226,101,221,133,226,232,169
STA 225
LDA 226
ADC BYTESMSB
STA 226
INX
LDA #0
.BYTE 0,201,0,240,191,96
CMP #0
BEQ #191
RTS

;Begin your assembly language program at line 1400
; To use the routine, just store your values into the appropriate variables and do a 'JSR GET' or 'JSR PUT'
BEGIN

Edited by JamesD
Link to comment
Share on other sites

No offense, but it's useless without doing positioning as well (i.e. to work with individual bytes in random-access files). To read or write files in their entirety, it's speedier to use the SIO routine. After opening the file, flow value ($07 for read, $0B for write) is placed in $352, start address goes in $354/5, length in $358/9. Load A and X with $10, then JMP $E456.

Link to comment
Share on other sites

GET/PUT in CIO can be byte or record oriented.

 

If the buffer length = 0 then it's a byte per call through the A register.

 

You'd be better off just consulting the OS manual.

 

CIO calls are pretty easy. X register = IOCB index (0, $10, $20 etc)

 

Then just set the command byte, buffer address, buffer length, aux1, aux2 when necessary before each call.

 

On return, Y contains the return code. >= $80 = error, 1=OK. In some situations, 2=last byte before EOF.

 

For OPEN you supply the filespec. Typical example .byte "E:",$9B

Link to comment
Share on other sites

CIO isn't really suited for screen I/O for moving objects.

 

For a softsprite, you'd need to position the cursor for each line then output the number of bytes = width of sprite. Sure, you can do record I/O but it's a time-wasting exercise. CIO calls the S: handler once per byte then S: throws away most of the byte just to get a single pixel colour.

 

I doubt you'd even get a single 4x8 pixel bitmap object moving in under a frame, and you've still to worry about the save/restore of background data.

 

You could benchmark CIO for a "best case scenario" - just OPEN S: with the graphics mode you want, then output a constant pixel value. You'd probably find the screen fills not much quicker in Assembly than if you had a couple of PUT #6 statements inside a loop in a Basic program.

Link to comment
Share on other sites

Anything through $e456 is CIO.

 

Resident device handlers are in a way part of CIO as well.

 

Whether you take the CIO route or deal direct with the handler, you're still going to have the speed problem.

 

Screen I/O is just too slow to be useful for anything other than a graphical adventure with low detail pictures.

Link to comment
Share on other sites

Anything through $e456 is CIO.

 

Resident device handlers are in a way part of CIO as well.

 

Whether you take the CIO route or deal direct with the handler, you're still going to have the speed problem.

 

Screen I/O is just too slow to be useful for anything other than a graphical adventure with low detail pictures.

You didn't answer my question. Where in the code does it call CIO?

Link to comment
Share on other sites

The code is practically unreadable. Byte directives which continue from the program.

 

The call doesn't necessarily have to go through $e456.

 

For the Put Character routine, the direct call sequence is to push the address onto the stack and do an RTS.

 

Anyway, what are you trying to do? Are you asking for help or just arguing the point that something uses CIO or not?

Link to comment
Share on other sites

I think the confusion came about because people were thinking this routine was an analog of the BASIC GET and PUT commands, which are CIO based. This function indeed doesn't have anything to do with I/O. However, either you're supposed to poke values into the code or it wasn't written by an experienced author, as it has a lot of wasteful CMP #0 instructions in it and a totally inexplicable LDA #0 / CMP #0 sequence.

 

	LDA #$00
	STA $D4
	STA $D5
	ORA ($D6,X)
	STA $E1
	LDA SAVMSC+1
	ADC $D7
	STA $E2
	LDX #$00
L5012   CPX $D8
	BEQ L5036
L5016   LDA $E1
	CLC
	ADC $DC
	STA $E1
	LDA $E2
	ADC $DD
	STA $E2
	LDA $D7
	SEC
	SBC #$01
	STA $D8
	LDA $D9
	SBC #$00
	STA $D9
	LDA #$00
	CMP #$00
	BEQ L5012
L5036   CPX $D9
	BEQ L5040
	LDA #$00
	CMP #$00
	BEQ L5016
L5040   LDX #$01
	LDY #$00
L5044   CPY $DA
	BEQ $5099
	LDA $E3
	CMP #$01
	BNE $5085
	LDA $E0
	CMP #$01
	BNE L506A
	LDA ($DE),Y
	CMP #$00
	BEQ L5064
	LDA ($E1),Y
	CMP #$00
	BEQ L5064
	LDA #$01
	STA $D4
L5064   LDA ($DE),Y
	CMP #$00
	BEQ L507E
L506A   LDA $E0
	CMP #$01
	BEQ L507A
	LDA ($E1),Y
	CMP #$00
	BEQ L507A
	LDA #$01
	STA $D4
L507A   LDA ($DE),Y
	STA ($E1),Y
L507E   INY
L507F   LDA #$00
	CMP #$00
	BEQ L5044
	LDA ($E1),Y
	CMP #$00
	BEQ L5091
	LDA #$01
	STA $D4
	LDA ($E1),Y
L5091  STA ($DE),Y
	LDA #$00
	CMP #$00
	BEQ L507E
	CPX $DB
	BEQ L50C0
	LDY #$00
	LDA $DE
	CLC
	ADC $DA
	STA $DE
	LDA $DF
	ADC #$00
	STA $DF
	LDA $E1
	CLC
	ADC $DC
	STA $E1
	LDA $E2
	ADC $DD
	STA $E2
	INX
	LDA #$00
	CMP #$00
	BEQ L507F
L50C0   RTS

Link to comment
Share on other sites

The code is practically unreadable. Byte directives which continue from the program.

The .BYTE directives were the original code, the hand disassembly for each BYTE line follows it.

 

The call doesn't necessarily have to go through $e456.

 

For the Put Character routine, the direct call sequence is to push the address onto the stack and do an RTS.

 

Anyway, what are you trying to do? Are you asking for help or just arguing the point that something uses CIO or not?

No, I'm trying to point out that you misunderstood what the code does and have refused to listen to what I'm saying. :(

 

It's a simple software sprite routine and it has nothing to do with CIO.

GET and PUT are not just I/O commands, they are also graphics commands in Extended Color Basic on the TRS-80 Color Computer and in IBM's GW BASIC.

GET grabs a square of pixels and sticks it in memory (BASIC uses an array).

PUT copies from that memory back to the screen.

The author was just trying to create something similar for the Atari.

 

I typed it in years ago when someone asked about software sprites for BASIC and I thought it might be a good start, but I forgot to post it.

 

Last night I had insomnia and started cleaning things off my old laptop.

I ran across the code and decided to hand disassemble the .BYTE portion of the code for something to do.

I thought I should post it before I forget about it again.

 

If someone is looking at doing software sprites, they might find it useful.

Link to comment
Share on other sites

I think the confusion came about because people were thinking this routine was an analog of the BASIC GET and PUT commands, which are CIO based. This function indeed doesn't have anything to do with I/O. However, either you're supposed to poke values into the code or it wasn't written by an experienced author, as it has a lot of wasteful CMP #0 instructions in it and a totally inexplicable LDA #0 / CMP #0 sequence.

Exactly, and yeah, I don't think he was that experienced.

<edit>

And the use of decimal instead of hex is probably a tipoff as to his experience as well.

 

 

I'm thinking the LDA #0/CMP #0 was caused by a typo on my part from entering the .BYTE sequence.

<edit>

on 2nd thought, he's probably using that for a branch always since he used it multiple times.

Edited by JamesD
Link to comment
Share on other sites

He doesn't seem to need the carry bit after those branches so the LDA #0/CMP #0/BEQ could be changed to CLC/BCC.

On the 65C02 you could just use BRA.

Or am I forgetting a shorter way? Been a couple years since I programmed the 6502.

Edited by JamesD
Link to comment
Share on other sites

lda #0 sets Z so you can always BEQ not needing the CMP #0.

 

Load instructions don't affect the carry.

But the LDA #0 isn't needed in the code and CLC takes fewer bytes/clocks.

I'm not disputing LDA #0/BEQ would work, just saying CLC/BCC is shorter/faster if you must do a branch always.

<edit>

The exception would be if you need to do an LDA #0 where you are branching to, but in this case, you have to make sure LDA #0 gets executed before you enter the loop the first time. One loop in the code might be able to use that.

 

<edit>

And I forgot, if you need the carry bit preserved, you can't exactly go clearing it.

Edited by JamesD
Link to comment
Share on other sites

Actually, after looking up the original article, it includes everything to use it from BASIC and includes some sample programs.

If someone is interested, they should read the article.

The code doesn't work exactly like GET/PUT from Extended Basic which worked at the pixel level, this is at the byte level.

 

Here is the Atari BASIC code. I looked through the opcodes and it appears to be different than the assembly version from the article.

The author probably tried to protect his code and extend the length of the article. Disassembling this code would probably be more useful.

The first 2 lines explain how to call the code. The article describes it in depth.

1000 REM D=USR(ADR(GP$)+GET,X,Y,WIDTH,LENGTH,BYTES PER ROW,ADR(IMAGE$))
1010 REM D=USR(ADR(GP$)+PUT,X,Y,WIDTH,LENGTH,BYTES PER ROW,ADR(IMAGE$),CMD)
1015 REM COPYRIGHT 1987 COMPUTE! PUBLICATIONS, INC.   ALL RIGHTS RESERVED
1020 DIM BP*(244):FOR I = 1 TO 244:READ CODE:GP$(I,I)=CHR$(CODE):NEXT I:LET GET=0:LET PUT=0:RETURN
1021 DATA 104,56,233,6,133,227,104,133,215,104,133,214,104,133,217,104
1022 DATA 133,216,104,104,133,219,104,133,221,104,133,220
1023 DATA 104,133,223,104,133,222,165,227,201,0,240,4,104,104,133,224
1024 DATA 169,0,133,212,133,213,165,88,24,101,214,133,225,165,89,101
1025 DATA 215,133,226,162,0,228,216,240,32,165,225,24,101,220,133,225
1026 DATA 165,226,101,221,133,226,165,216,56,233,1,133,216,165,217,233
1027 DATA 0,133,217,169,0,201,0,240,220,228,217,240,6,169,0,201
1028 DATA 0,240,214,162,1,160,0,196,218,240,81,165,227,201,1,208
1029 DATA 55,165,224,201,1,208,22,177,222,201,0,240,10,177,225,201
1030 DATA 0,240,4,169,1,133,212,177,222,201,0,240,20,165,224,201
1031 DATA 1,240,10,177,225,201,0,240,4,169,1,133,212,177,222,145
1032 DATA 225,200,169,0,201,0,240,191,177,225,201,0,240,6,169,1
1033 DATA 133,212,177,225,145,222,169,0,201,0,240,229,228,219,240,35
1034 DATA 160,0,165,222,24,101,218,133,222,165,223,105,0,133,223,165
1035 DATA 225,24,101,220,133,225,165,226,101,221,133,226,232,169,0,201
1036 DATA 0,240,191,96

Edited by JamesD
Link to comment
Share on other sites

  • 4 weeks later...

I had a few minutes to mess with this last night. I fixed some typos and missing numbers from the code posted above and included one of the demos.

The disassembly is only a little different than before. The code for BASIC pops parameters from the stack.

If someone actually decides to use it, the code could be optimized to save some clock cycles.

5 REM COPYRIGHT 1987 COMPUTE! PUBLICATIONS, INC. ALL RIGHTS RESERVED.
8 PRINT "{CLEAR}COPYRIGHT 1987":PRINT "COMPUTE! PUBLICATIONS, INC.":PRINT "ALL RIGHTS RESERVED."
10 DIM A$(3*10*40):A$=CHR$(0):A$(3*10*40)=CHR$(0):A$(2)=A$
20 GOSUB 1000:I=0:GOTO 65
30 GRAPHICS 7:SETCOLOR 2,8,0:COLOR 3
40 PLOT 9,9:PLOT 0,0:DRAWTO 39,0:DRAWTO 39,P*5+1:DRAWTO 19,P*5+1:DRAWTO 19,40-P*5:DRAWTO 39,40-P*5:DRAWTO 39,39
50 DRAWTO 0,39:DRAWTO 0,0
60 D=USR(ADR(GP$)+GET,0,0,10,40,40,ADR(A$)+(I-1)*10*40)
65 I=I+1
70 IF I=1 THEN P=2:GOTO 30
71 IF I=2 THEN P=3:GOTO 30
72 IF I=3 THEN P=4:GOTO 30
80 GRAPHICS 7:SETCOLOR 2,8,0:X=14:Y=19:I=1:DI=1
90 D=USR(ADR(GP$)+PUT,X,Y,10,40,40,ADR(A$)+(I-1)*10*40,0)
100 IF STRIG(0)<>0 THEN 100
110 I=I+DI
120 IF DI=1 THEN IF I=3 THEN DI=-1:GOTO 140
130 IF DI=-1 THEN IF I=1 THEN DI=1
140 FOR DE=1 TO 30:NEXT DE:GOTO 90

1000 REM D=USR(ADR(GP$)+GET,X,Y,WIDTH,LENGTH,BYTES PER ROW,ADR(IMAGE$))
1010 REM D=USR(ADR(GP$)+PUT,X,Y,WIDTH,LENGTH,BYTES PER ROW,ADR(IMAGE$),CMD)
1015 REM COPYRIGHT 1987 COMPUTE! PUBLICATIONS, INC.   ALL RIGHTS RESERVED
1020 DIM GP$(244):FOR I = 1 TO 244:READ CODE:GP$(I,I)=CHR$(CODE):NEXT I:LET GET=0:LET PUT=0:RETURN
1021 DATA 104,56,233,6,133,227,104,133,215,104,133,214,104,133,217,104
1022 DATA 133,216,104,104,133,218,104,104,133,219,104,133,221,104,133,220
1023 DATA 104,133,223,104,133,222,165,227,201,0,240,4,104,104,133,224
1024 DATA 169,0,133,212,133,213,165,88,24,101,214,133,225,165,89,101
1025 DATA 215,133,226,162,0,228,216,240,32,165,225,24,101,220,133,225
1026 DATA 165,226,101,221,133,226,165,216,56,233,1,133,216,165,217,233
1027 DATA 0,133,217,169,0,201,0,240,220,228,217,240,6,169,0,201
1028 DATA 0,240,214,162,1,160,0,196,218,240,81,165,227,201,1,208
1029 DATA 55,165,224,201,1,208,22,177,222,201,0,240,10,177,225,201
1030 DATA 0,240,4,169,1,133,212,177,222,201,0,240,20,165,224,201
1031 DATA 1,240,10,177,225,201,0,240,4,169,1,133,212,177,222,145
1032 DATA 225,200,169,0,201,0,240,191,177,225,201,0,240,6,169,1
1033 DATA 133,212,177,225,145,222,169,0,201,0,240,229,228,219,240,35
1034 DATA 160,0,165,222,24,101,218,133,222,165,223,105,0,133,223,165
1035 DATA 225,24,101,220,133,225,165,226,101,221,133,226,232,169,0,201
1036 DATA 0,240,191,96

Link to comment
Share on other sites

A little demo of my own. Just playing around with it to hint at the potential. Add lines 1000-1036 from the listing above.

10 SIZE = 7*24
20 DIM A$(SIZE):A$=CHR$(0):A$(SIZE)=CHR$(0):A$(2)=A$
30 GOSUB 1000
40 GRAPHICS 7:SETCOLOR 0,1,0:SETCOLOR 1,6,3:SETCOLOR 2,3,4:SETCOLOR 3,10,5
50 COLOR 2:PLOT 8,0:DRAWTO 16,0:DRAWTO 24,8:DRAWTO 16,16:DRAWTO 8,16:DRAWTO 0,8:DRAWTO 8,0
60 D=USR(ADR(GP$)+GET,0,0,7,24,40,ADR(A$))
70 FOR I=1 TO 8:READ X:READ Y
80 D=USR(ADR(GP$)+PUT,X,Y,7,24,40,ADR(A$),1)
90 NEXT I
140 GOTO 140
10000 DATA 0,0,4,8,8,16,12,24,0,16,4,24,8,32,12,40

Link to comment
Share on other sites

I have zero experience with Atari BASIC prior to this and the code in this post is untested at this point, but it should be at least close to working.

 

By storing the USR code on disk along with it's length and just reading it from the file in your own code it allows alteration of the USR routine with no modifications to any program that uses it. I suppose you could just get the file length instead of storing the length in the file, but by doing this you could store multiple USR routines in a single file.

This also has the added benefit of not requiring memory for DATA statements in the code which take more space than the actual USR code.

 

 

Routine to write the USR data to a disk file.

Line 1015 should be altered to calculate the LSB and MSB of the length if LEN is over 255.

Attach the DATA code section lines 1021-1036 from above to complete it.

1000 REM WRITE THE LENGTH OF THE USR ROUTINE TO A FILE FOLLOWED BY THE USR ROUTINE
1010 LEN = 244
1015 OPEN #1,8,0,"D:GETPUT.USR": PUT #1,LEN:PUT#1,0 :REM 16 BIT LENGTH OF USR ROUTINE
1020 FOR I=1 TO LEN:READ CODE:PUT #1,CHR$(CODE):NEXT I :CLOSE #1:RETURN

 

Read in the USR code with Atari BASIC.

1000 OPEN #1,4,0,"D:GETPUT.USR": GET #1,LENL : GET #1,LENH : LET LEN =LENL + (256*LENH)
1010 FOR I=1 TO LEN:GET #1,CODE:POKE GP$(I,I)=CHR$(CODE):NEXT I:LET GET=0:LET PUT=0
1020 CLOSE #1:RETURN

 

Read the USR code with BASIC XL, BASIC XE, or Turbo BASIC.

The GP$(LEN)=CHR$(0) is there to let basic know the length of the string but I don't know if it's required.

1000 OPEN #1,4,0,"D:GETPUT.USR": GET #1,LENL : GET #1,LENH : LET LEN =LENL + (256*LENH)
1010 DIM GP$(LEN):GP$(LEN)=CHR$(0):BGET #1,ADR(GP$),LEN:LET GET=0:LET PUT=0
1020 CLOSE #1:RETURN

Link to comment
Share on other sites

I got the basis for this from another thread.

It should read the entire USR function in one shot from ATARI BASIC if the original code works as advertised, the read continues from where the GET statements left off, and the AND works the same as other BASIC dialects.

I have not tested it yet. 1020-1040 should probably be a separate subroutine if you want to read more than this USR routine in.

1000 OPEN #1,4,0,"D:GETPUT.USR": GET #1,LENL : GET #1,LENH :REM LET LEN =LENL + (256*LENH)
1010 LET ADDRL = ADR(GP$) AND 255: LET ADDRH = ADR(GP$) AND 65280
1020 POKE 852,ADDRL:POKE 853,ADDRH:REM Put low and high address bytes in IOCB #1
1030 POKE 856,LENL:POKE 857,LENH:REM Ditto for low and high length bytes
1040 POKE 850,7:I=USR(ADR("hhh*LVd"),16):REM * and d are inverse video, 7 is the "get" command, 16 is IOCB #1
1050 CLOSE #1:RETURN

Link to comment
Share on other sites

Routine to write the USR data to a disk file.

Line 1015 should be altered to calculate the LSB and MSB of the length if LEN is over 255.

Attach the DATA code section lines 1021-1036 from above to complete it.

1000 REM WRITE THE LENGTH OF THE USR ROUTINE TO A FILE FOLLOWED BY THE USR ROUTINE
1010 LEN = 244
1015 OPEN #1,8,0,"D:GETPUT.USR": PUT #1,LEN:PUT#1,0 :REM 16 BIT LENGTH OF USR ROUTINE
1020 FOR I=1 TO LEN:READ CODE:PUT #1,CHR$(CODE):NEXT I :CLOSE #1:RETURN

 

This was the quickest way I could think of to implement the code to calculate the LSB and MSB of the length of the USR routine automatically.

It uses DIV and MOD which just about every BASIC other than standard ATARI BASIC supports.

The LSB and MSB could be calculated by hand and hard coded in like the original version of the code above if you want to run it on Atari BASIC.

1000 REM DIV AND MOD ARE NOT AVAILABLE IN STANDARD ATARI BASIC.  PICK ANY OTHER BACKWARDS COMPATIBLE BASIC TO RUN THIS
1010 LEN = 244: REM 1015 SHOULD BE ALTERED TO CALCULATE 16 BIT # BYTES IF LEN > 255
1015 OPEN #1,8,0,"D:GETPUT.USR": PUT #1,LEN MOD 256:PUT#1,LEN DIV 256 :REM 16 BIT LENGTH OF USR ROUTINE
1020 FOR I=1 TO LEN:READ CODE:PUT #1,CHR$(CODE):NEXT I :CLOSE #1:RETURN

Edited by JamesD
Link to comment
Share on other sites

Yep, no DIV or MOD but they're not exactly widespread.

AND is common though but Atari Basic only uses AND/OR/NOT in a simple boolean mode.

 

The common way of doing such things is:

 

H = INT(A/256) : L = A-H*256

 

I suppose if you had widespread need to get hi/lo bytes throughout a program you could do it in a couple of USR routines.

 

MOD256$ = " pla / pla / pla / sta $d4 / lda #0 / sta $d5 / rts "

DIV256$ = "pla / pla / tax / pla / stx $d5 / lda #0 / sta $d4 / rts "

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