The BEST QL Editor

A place to discuss general QL issues.
User avatar
Derek_Stewart
Font of All Knowledge
Posts: 4869
Joined: Mon Dec 20, 2010 11:40 am
Location: Sunny Runcorn, Cheshire, UK

Re: The BEST QL Editor

Post by Derek_Stewart »

Hi,

If the Pointer System is so bad, then what should I be using?


Regards, Derek
User avatar
ql_freak
Super Gold Card
Posts: 615
Joined: Sun Jan 18, 2015 1:29 am

Re: The BEST QL Editor

Post by ql_freak »

The software from many called Pointer Interface (this is just the part which allows for a mouse pointer and none destructable windows) is since Qpac2 the Extended Environment and includes the Pointer Interface, Wman (Window Manager) which allows sub windows etc., the Thing system AND the Hotkey System (Great). This all plays together and it is GREAT!

Of course it is different from all currently used GUIs. But that's okay. The Exteded Environment has been implemented when GUIs where invented. Tony Tebby has had another approach, which is IMHO better, than the GUIs you are using (must use) nowadays. E.g. the Hit (left mouse button) and Do (right mouse button). With Do normally a menu pops up, as if you e.g. click the right mouse button in the Windows File Explorer, i.e. a (now called) context menu. I have seen this first on the QL, Windows have had this feature (for some programs) 1 or 2 years later. But it's even better: If you do a Hit in a menu of a Qpac2 program (menu), the menu stays open and the other program starts. If you do a Do, the current Qpac2 menu, the menu disappears, and the menu you have clicked opens.

Then there's the excellent HOT_KEY system (ERT HOT_KEY(...)) with it's hotkey stuffer!!! Have you ever tried to get the full pathname of a file listed in the File Explorer of Windows(!). In the Files menu of QPac2 you just have to click on the file (directory) and you can insert the full pathname with <ALT>–<SPACE> (and the one before with <SHIFT>–<ALT>–<SPACE>!) in any prompt of a program, which requires a file name. That's the reason, why I normally do not use (need) Jochen Merz's file chooser extension (I can do it IMHO more easy with the Files menu).


http://peter-sulzer.bplaced.net
GERMAN! QL-Download page also available in English: GETLINE$() function, UNIX-like "ls" command, improved DIY-Toolkit function EDLINE$ - All with source. AND a good Python 3 Tutorial (German) for Win/UNIX :-)
User avatar
bwinkel67
QL Wafer Drive
Posts: 1609
Joined: Thu Oct 03, 2019 2:09 am

Re: The BEST QL Editor

Post by bwinkel67 »

ql_freak wrote: Sat Sep 20, 2025 1:15 am The Exteded Environment has been implemented when GUIs where invented.
I didn't realize this was over 40 years old. I thought it was created in the 90s, not early 80s. Did Tony create it at the same time as TK2?


User avatar
Derek_Stewart
Font of All Knowledge
Posts: 4869
Joined: Mon Dec 20, 2010 11:40 am
Location: Sunny Runcorn, Cheshire, UK

Re: The BEST QL Editor

Post by Derek_Stewart »

Hi,

I am sorry to imply that I did not know what the Pointer System is, which I meant: PTR_GEN, WMAN, HOT_REXT, and QPAC2, which make it The Extended Environment.

I used all this since Qjump introduced QRAM, which I bought and he update QPAC2, which included the Pointer System. This is all free now.

If I want to use a GUI system on the QL, there is only one option - The Pointer System and QPAC2, QD, QMENU, QBASIC which makes the QL a nice GUI based system in less than 512K or ram, no other system can do this.

The best non GUI editor in my opinion is QED, and maybe TheEDitorSE a close second. GUI Editors, QD, I can not think of any other

But then all the software is free, so have it all and decide which is our best option.

Again my question is, if the Pointer System written by Tony Tebby, is so bad, as he seemed to write good software. What should I be using?


Regards, Derek
User avatar
tofro
Font of All Knowledge
Posts: 3214
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: The BEST QL Editor

Post by tofro »

bwinkel67 wrote: Sat Sep 20, 2025 6:22 am
ql_freak wrote: Sat Sep 20, 2025 1:15 am The Exteded Environment has been implemented when GUIs where invented.
I didn't realize this was over 40 years old. I thought it was created in the 90s, not early 80s. Did Tony create it at the same time as TK2?
Digging a bit in memory and old paperwork:
Its first incarnation came with QRAM, and that should have been 1987 or 1988 - So, not quite 40 years old. But to establish itself from a GUI toolkit that drove QJump programs to the publicly accepted WIMP for the QL took another, like 3-5 years. I can't just now find the first release date of the QPTR toolkit that provided the API to third-party developers.

TK2 is a different story - that entered the market as early as 1985, one year after the launch.


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
User avatar
pjw
QL Wafer Drive
Posts: 1659
Joined: Fri Jul 11, 2014 8:44 am
Location: Norway
Contact:

Re: The BEST QL Editor

Post by pjw »

tofro wrote: Sat Sep 20, 2025 7:45 am
bwinkel67 wrote: Sat Sep 20, 2025 6:22 am
ql_freak wrote: Sat Sep 20, 2025 1:15 am The Exteded Environment has been implemented when GUIs where invented.
I didn't realize this was over 40 years old. I thought it was created in the 90s, not early 80s. Did Tony create it at the same time as TK2?
Digging a bit in memory and old paperwork:
Its first incarnation came with QRAM, and that should have been 1987 or 1988 - So, not quite 40 years old. But to establish itself from a GUI toolkit that drove QJump programs to the publicly accepted WIMP for the QL took another, like 3-5 years. I can't just now find the first release date of the QPTR toolkit that provided the API to third-party developers.
<>
I dont know when QPTR was first released to the public, but from the original source code (V0.00) it looks like QJump's JR Oakley wrote most of it back in 1987.


Per
I love long walks, especially when they are taken by people who annoy me.
- Fred Allen
User avatar
RalfR
QL Wafer Drive
Posts: 1272
Joined: Fri Jun 15, 2018 8:58 pm

Re: The BEST QL Editor

Post by RalfR »

pjw wrote: Sat Sep 20, 2025 8:35 amI dont know when QPTR was first released to the public, but from the original source code (V0.00) it looks like QJump's JR Oakley wrote most of it back in 1987.
The first ad for QPTR was published in QL-World in May 1987.

And yes, QPTR was written by Jonathan Oakley; in retrospect, some things could certainly have been done differently and more simply. Apart from Wolfgang Lenerz, I don't know anyone who uses QPTR. I used it once and found it extremely confusing and complicated. Never again. QPTR was certainly well-intentioned, but from a commercial perspective, I think it was a disaster. Jonathan Oakley probably spent weeks if not months on it.

As I gathered from my letters from TT, the Pointer Interface came first; the Window Manager came much later and, theoretically, isn't necessary for simpler things. TT hasn't commented on whether this was already started under Sinclair (although I've asked several times).

Would TT do the Pointer Environment the same way again today? After all, some things were changed before the initial release. For example, in the beginning, the secondaries could be located outside the primary. That would be very nice these days and is common practice with other GUIs.

The new abbreviations (e.g. SMS.INFO vs. MT.INF) are already present in the source files of the Pointer Environment and QPTR. This suggests that the SMS kernel was either finished very early (1985) or that someone took the trouble to replace the abbreviations everywhere, which would have been a huge task if it wasn't done automatically.

BTW: The pointer interface (ptr_gen) was changed and expanded every few weeks during the QRAM era. That's why I never understood why Sandy put it into the EPROM of his SuperQBoard/Mouse. Even the first QRAM version shipped with the SQB had a more up-to-date ptr_gen. So you had to load it anyway. At least that's how it was with my SQB.


7000 4E75
User avatar
dilwyn
Mr QL
Posts: 3185
Joined: Wed Dec 01, 2010 10:39 pm

Re: The BEST QL Editor

Post by dilwyn »

NormanDunbar wrote: Fri Sep 19, 2025 5:11 pm
SandySuperQDave wrote:My actual question: Is there a list of S*BASIC tokens anywhere?
There is indeed. I have one but as I literally moved house a couple of days ago, I know not where it is!

Somewhere, I have a program written in Super basic to decide a QSaved file back into plain text. That file, which is well commented, should give you all the token details you need.

Sorry Im not much help here, I do know Dilwyn updated it to cope with the new Binary and Hexadecimal floating point values, perhaps he can help?

Cheers,
Norm.
Can you remember the filename of it? I'll have a look.

Page 198 of the Jan Jones SuperBASIC book, after floating point format tokens, I have the following handwritten:

* HEX values: six bytes $E masked into top four bits
* BIN values: six bytes $D masked into top four bits
* stored as floating point, but withtop 4 bits changed to indicate HEX or BINary values.


User avatar
dilwyn
Mr QL
Posts: 3185
Joined: Wed Dec 01, 2010 10:39 pm

Re: The BEST QL Editor

Post by dilwyn »

dilwyn wrote: Sat Sep 20, 2025 11:45 am
NormanDunbar wrote: Fri Sep 19, 2025 5:11 pm
SandySuperQDave wrote:My actual question: Is there a list of S*BASIC tokens anywhere?
There is indeed. I have one but as I literally moved house a couple of days ago, I know not where it is!

Somewhere, I have a program written in Super basic to decide a QSaved file back into plain text. That file, which is well commented, should give you all the token details you need.

Sorry Im not much help here, I do know Dilwyn updated it to cope with the new Binary and Hexadecimal floating point values, perhaps he can help?

Cheers,
Norm.
Can you remember the filename of it? I'll have a look.

Page 198 of the Jan Jones SuperBASIC book, after floating point format tokens, I have the following handwritten:

* HEX values: six bytes $E masked into top four bits
* BIN values: six bytes $D masked into top four bits
* stored as floating point, but withtop 4 bits changed to indicate HEX or BINary values.
Possibly this - looks like it needs Norman's DJtoolkit:

Code: Select all

100 REMark _SAV file decoder
110 :
120 CLS
130 PRINT 'SAV File Decoder'\\
140 INPUT 'Which _sav file ? ';sav$
150 IF sav$ = '' THEN STOP: END IF
160 :
170 initialise
180 decode_header
190 IF NOT _quit
200    decode_name_table
210    decode_program
220 END IF
230 RELEASE_HEAP float_buffer
240 CLOSE #3
250 :
260 DEFine PROCedure decode_header
270   LOCal head$(4), name_table_length
280   _quit = 0
290   OPEN_IN #3,sav$
300   head$ = FETCH_BYTES(#3, 4)
310   IF (head$ <> 'Q1' & CHR$(0) & CHR$(0)) AND (head$ <> 'Q1' & CHR$(2) & CHR$(192)) AND (head$ <> 'Q1'& CHR$(3) & CHR$(128))
320   PRINT head$, head$(1);head$(2)!!CODE(head$(3))!CODE(head$(4))\
330      PRINT sav$ & ' is not a SAV file, or has a new flag.'
340      CLOSE #3
350      _quit = 1
360      RETurn
370   END IF
380   name_table_entries = GET_WORD(#3)
390   name_table_length = GET_WORD(#3)
400   program_lines = GET_WORD(#3)
410   max_name_size = name_table_length - (4 * name_table_entries) / name_table_entries
420   :
430   PRINT sav$
440   PRINT 'Number of name table entries : '; name_table_entries
450   PRINT 'Name table length            : '; name_table_length
460   PRINT 'Number of program lines      : '; program_lines
470   PRINT
480   :
490   DIM name_table$(name_table_entries -1, max_name_size)
500   float_buffer = RESERVE_HEAP(6)
510   _quit = (float_buffer < 1)
520 END DEFine decode_header
530 :
540 DEFine PROCedure decode_name_table
550   LOCal x, name_type, line_no, name_length, name$, lose_it$(1)
560   LOCal num_procs, num_fns
570   num_procs = 0
580   num_fns = 0
590   FOR x = 0 TO name_table_entries -1
600     name_type = GET_WORD(#3)
610     line_no = GET_WORD(#3)
620     name_length = GET_WORD(#3)
630     name$ = FETCH_BYTES(#3, name_length)
640     IF name_length && 1
650        lose_it$ = INKEY$(#3)
660     END IF
670     IF name_type = 5122 THEN num_procs = num_procs + 1
680     IF name_type >= 5377 AND name_type <= 5379
690        num_fns = num_fns + 1
700     END IF
710     PRINT x;'  Name type = '; HEX$(name_type, 16) & '  ';
720     PRINT 'Line number = '; line_no & '  ';
730     PRINT 'Name length = '; name_length; '  ';
740     PRINT 'Name = <' & name$ & '>'
750     name_table$(x) = name$
760   END FOR x
770   PRINT 'There are ' & num_procs & ' PROCs'
780   PRINT 'There are ' & num_fns & ' FNs'
790 END DEFine decode_name_table
800 :
810 :
820 DEFine PROCedure decode_program
830   LOCal x, type_byte, program_line
840   :
850   REMark WORD = size change
860   REMark LONG = $8D00.line number
870   REMark rest of line
880   :
890   REPeat program_line
900     IF EOF(#3) THEN EXIT program_line: END IF
910     line_size = line_size + GET_WORD(#3)
920     IF line_size > 65536 THEN line_size = line_size - 65536: END IF
930     IF GET_WORD(#3) <> HEX('8d00')
940        PRINT 'Program out of step.'
950        CLOSE #3
960        STOP
970     END IF
980     PRINT GET_WORD(#3); ' ';
990     line_done = 0
1000     REPeat line_contents
1010       type_byte = CODE(INKEY$(#3))
1020       SELect ON type_byte
1030         = HEX('80'): multi_spaces
1040         = HEX('81'): keywords
1050         = HEX('84'): symbols
1060         = HEX('85'): operators
1070         = HEX('86'): monadics
1080         = HEX('88'): names
1090         = HEX('8B'): strings
1100         = HEX('8C'): text
1110         = HEX('8E'): separators
1120         = HEX('D0') TO HEX('DF') : floating_points 1 : REMark % binary number
1130         = HEX('E0') TO HEX('EF') : floating_points 2 : REMark $ hex number
1140         = HEX('F0') TO HEX('FF') : floating_points 3 : REMark floating point
1150       END SELect
1160       IF line_done THEN EXIT line_contents: END IF
1170     END REPeat line_contents
1180   END REPeat program_line
1190 END DEFine decode_program
1200 :
1210 :
1220 DEFine PROCedure multi_spaces
1230   :
1240   REMark $80.nn = print nn spaces
1250   :
1260   PRINT FILL$(' ', GET_BYTE(#3));
1270 END DEFine multi_spaces
1280 :
1290 :
1300 DEFine PROCedure keywords
1310   :
1320   REMark $81.nn = keyword$(nn)
1330   :
1340   PRINT keyword$(GET_BYTE(#3));' ';
1350 END DEFine keywords
1360 :
1370 :
1380 DEFine PROCedure symbols
1390   LOCal sym
1400   :
1410   REMark $84.nn = symbol$(nn)
1420   :
1430   sym = GET_BYTE(#3)
1440   PRINT symbol$(sym);
1450   line_done = (sym = 10)
1460 END DEFine symbols
1470 :
1480 :
1490 DEFine PROCedure operators
1500   :
1510   REMark $85.nn = operator$(nn)
1520   :
1530   PRINT operator$(GET_BYTE(#3));
1540 END DEFine operators
1550 :
1560 :
1570 DEFine PROCedure monadics
1580   :
1590   REMark $86.nn = monadic$(nn)
1600   :
1610   PRINT monadic$(GET_BYTE(#3));
1620 END DEFine monadic
1630 :
1640 :
1650 DEFine PROCedure names
1660   LOCal ignore
1670   :
1680   REMark $8800.nnnn = name_table$(nnnn)
1690   :
1700   ignore = GET_BYTE(#3)
1710   ignore = GET_WORD(#3)
1720   IF ignore > 32768 THEN ignore = ignore - 32768: END IF
1730   PRINT name_table$(ignore);
1740 END DEFine names
1750 :
1760 :
1770 DEFine PROCedure strings
1780   LOCal delim$(1), size
1790   :
1800   REMark $8B.delim.string_size = 'delim'; string; 'delim'
1810   :
1820   delim$ = INKEY$(#3)
1830   size = GET_WORD(#3)
1840   PRINT delim$; FETCH_BYTES(#3, size); delim$;
1850   IF size && 1
1860      size = GET_BYTE(#3)
1870   END IF
1880 END DEFine strings
1890 :
1900 :
1910 DEFine PROCedure text
1920   LOCal size
1930   :
1940   REMark $8C00.size = text
1950   :
1960   size = GET_BYTE(#3)
1970   size = GET_WORD(#3)
1980   PRINT FETCH_BYTES(#3, size);
1990   IF size && 1
2000      size = GET_BYTE(#3)
2010   END IF
2020 END DEFine text
2030 :
2040 :
2050 DEFine PROCedure separators
2060   :
2070   REMark $8E.nn = separator$(nn)
2080   :
2090   PRINT separator$(GET_BYTE(#3));
2100 END DEFine separators
2110 :
2120 :
2130 DEFine PROCedure floating_points (fp_type)
2140   REMark modified for % and $ SBASIC values 22.01.10 - DJ
2150   LOCal number$(6),fpt
2160   fpt = fp_type : REMark to avoid SEL ON last parameter issue later
2170   :
2180   REMark fp_type=...
2190   REMark $Dx.xx.xx.xx.xx.xx - %binary number
2200   REMark $Ex.xx.xx.xx.xx.xx - $hex number
2210   REMark $Fx.xx.xx.xx.xx.xx - need to mask out the first $F !
2220   :
2230   MOVE_POSITION #3, -1: REMark back up to the first byte
2240   number$ = FETCH_BYTES(#3, 6)
2250   number$(1) = CHR$( CODE(number$(1)) && 15)
2260   POKE_STRING float_buffer, number$
2270   SELect ON fpt
2280     =1 : PRINT '%';LTrim$(BIN$(PEEK_FLOAT(float_buffer),32));
2290     =2 : PRINT '$';LTrim$(HEX$(PEEK_FLOAT(float_buffer),32));
2300     =3 : PRINT PEEK_FLOAT(float_buffer);
2310   END SELect
2320 END DEFine floating_points
2330 :
2340 DEFine FuNction LTrim$(str$)
2350   REMark added 22.01.10 for % and $ values - DJ
2360   REMark remove leading zeros from binary or hex strings
2370   LOCal a,t$
2380   t$ = str$ : REMark full length by default
2390   FOR a = 1 TO LEN(t$)
2400     IF t$(a) <> '0' THEN t$ = t$(a TO LEN(t$)) : EXIT a
2410   NEXT a
2420     t$ = '0' : REMark in case it was all zeros
2430   END FOR a
2440   RETurn t$
2450 END DEFine LTrim$
2460 :
2470 DEFine PROCedure initialise
2480   LOCal x
2490   :
2500   _quit = 0
2510   last_line_size = 0
2520   line_size = 0
2530   name_table_entries = 0
2540   :
2550   RESTORE 2580
2560   DIM keyword$(31, 9)
2570   FOR x = 1 TO 31: READ keyword$(x): END FOR x
2580   DATA 'END', 'FOR', 'IF', 'REPeat', 'SELect', 'WHEN', 'DEFine'
2590   DATA 'PROCedure', 'FuNction', 'GO', 'TO', 'SUB', '', 'ERRor', ''
2600   DATA '', 'RESTORE', 'NEXT', 'EXIT', 'ELSE', 'ON', 'RETurn'
2610   DATA 'REMAINDER', 'DATA', 'DIM', 'LOCal', 'LET', 'THEN', 'STEP'
2620   DATA 'REMark', 'MISTake'
2630   :
2640   DIM symbol$(10)
2650   symbol$ =  '=:#,(){} ' & CHR$(10)
2660   :
2670   DIM operator$(22, 5)
2680   FOR x = 1 TO 22: READ operator$(x): END FOR x
2690   DATA '+', '-', '*', '/', '>=', '>', '==', '=', '<>', '<=', '<'
2700   DATA '||', '&&', '^^', '^', '&', 'OR', 'AND', 'XOR', 'MOD'
2710   DATA 'DIV', 'INSTR'
2720   :
2730   DIM monadic$(4, 3)
2740   FOR x = 1 TO 4: READ monadic$(x): END FOR x
2750   DATA '+', '-', '~~', 'NOT'
2760   :
2770   DIM separator$(5, 2)
2780   FOR x = 1 TO 5: READ separator$(x): END FOR x
2790   DATA ',', ';', '\', '!', 'TO'
2800   :
2810 END DEFine initialise
2820 :
Last edited by dilwyn on Sat Sep 20, 2025 1:31 pm, edited 1 time in total.
Reason: Added note program needs DJtoolkit.


User avatar
tofro
Font of All Knowledge
Posts: 3214
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: The BEST QL Editor

Post by tofro »

dilwyn wrote: Sat Sep 20, 2025 11:45 am
NormanDunbar wrote: Fri Sep 19, 2025 5:11 pm
SandySuperQDave wrote:My actual question: Is there a list of S*BASIC tokens anywhere?

Page 198 of the Jan Jones SuperBASIC book, after floating point format tokens, I have the following handwritten:

* HEX values: six bytes $E masked into top four bits
* BIN values: six bytes $D masked into top four bits
* stored as floating point, but withtop 4 bits changed to indicate HEX or BINary values.
QDOS/SMSQE Reference manual, page 18-11:

Code: Select all

18.5.2. BASIC Token Values
The following Section defines the token values used for the internal storage of a S*Basic program.
tkb.space $80 spaces in the listing - two bytes: token, count
tkw.keyw $81 all sorts of keywords:
tkw.end $8101 END
tkw.for $8102 FOR
tkw.if $8103 IF
tkw.rep $8104 REPeat
tkw.sel $8105 SELect
tkw.when $8106 WHEN
tkw.def $8107 DEFine
tkw.proc $8108 PROCedure
tkw.fn $8109 FuNction
tkw.go $810A GO
tkw.to $810B TO
tkw.sub $810C SUB
tkw.err $810E ERRor
tkw.rest $8111 RESTORE
tkw.next $8112 NEXT
tkw.exit $8113 EXIT
tkw.else $8114 ELSE
tkw.on $8115 ON
tkw.ret $8116 RETurn
tkw.rmdr $8117 REMAINDER
tkw.data $8118 DATA
tkw.dim $8119 DIM
tkw.loc $811A LOCal
tkw.let $811B LET
tkw.then $811C THEN
tkw.step $811D STEP
tkw.rem $811E REMark
tkw.mist $811F MISTake
tkb.odds $84 All sorts of separators:
tkw.lequ $8401 (LET) =
tkw.coln $8402 :
tkw.hash $8403 #
tkw.comma $8404 ,
tkw.lpar $8405 (
QDOS/SMS Reference Manual v. 4.9 31.03.2025 Section 18 - 9
tkw.rpar $8406 )
tkw.lbrc $8407 {
tkw.rbrc $8408 }
tkw.space $8409 Space (significant)
tkw.eol $840A End of line
tkb.oper $85 All sorts of operators:
tkw.plus $8501 +
tkw.minus $8502 -
tkw.mulf $8503 *
tkw.divf $8504 /
tkw.ge $8505 >=
tkw.gt $8506 >
tkw.apeq $8507 ==
tkw.eq $8508 =
tkw.ne $8509 <>
tkw.le $850A <=
tkw.lt $850B <
tkw.bor $850C
tkw.band $850D &&
tkw.bxor $850E ^^
tkw.power $850F ^
tkw.cnct $8510 &
tkw.or $8511 OR
tkw.and $8512 AND
tkw.xor $8513 XOR
tkw.mod $8514 MOD
tkw.div $8515 DIV
tkw.instr $8516 INSTR
tkw.neg $8601 Negate
tkw.pos $8602 Positive!!
tkw.bnot $8603 ~~
tkw.not $8604 ~
tkb.name
$8800 Name: The name token is followed by a word index to the name table
tkw.quote $8B22 String delimited by "quotes"
tkw.apost $8B27 String delimited by 'apostrophes'
tkw.text $8C00 Text (after REMark)The string and text tokens are followed by a word (nr. of
chars) and the characters (with a pad byte if odd)
tkb.lno $8D00 line number (word)
tkb.seps $8E All sorts of formatting separators:
tkw.scoma $8E01 Separator comma
tkw.scoln $8E02 Semicolon
tkw.bslsh $8E03 Backslash
tkw.bar $8E04 Bar
tkw.sto $8E05 Separator TO


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
Post Reply