Jump to content
Sign in to follow this  
576XE

Native Pascal and PL65. I've lost.

Recommended Posts

Hello dear friends!
Please look at this pl65 code.
It compiles and works like a charm.
! Inverse string assembly subroutine !
CONST minC=0,maxC=39,minR=0,maxR=23
CONST None=255,Esc=28

INT SAVMSC=$58,linAdr[24]
INT _Adr=$C0 BYTE _Len=$C2

!= Procedures & Functions ===========!
!====================================!

!= Miscellaneous Procedures =========!
!- Wait for Any Key Pressed ---------!
FUNC readKey()
  CONST None=255
  BYTE ch=764,res
BEGIN
  WHILE ch=None DO res=ch ENDWHILE
  ch=None
END res

!= Block Oriented Procedures ========!

!- Initialization of DMA ------------!
PROC initVid()
  INT j
BEGIN
  FOR j=minR TO maxR DO
    linAdr[j]=j*40+SAVMSC
  NEXT
END

!- Inverse Screen Line --------------!
!- Needs initialization of Globals --!
!- i.e. _Adr & _Len = 0 -------------!
PROC invLine(INT a BYTE l)
BEGIN
!- Move Parms to Globals ------------!
  _Adr=a _Len=l

        LDY #$00
:Loop   LDA (_Adr),Y
        EOR #$80
        STA (_Adr),Y
        INY
        CPY _Len
        BNE Loop
END

!= Main Procedure ===================!
MAIN()
  BYTE x,y,l,k INT a
BEGIN
  initVid()
  x=0 y=0
  a=linAdr[y]+x l=40

  WHILE k<>Esc DO
!- Is AnyKey pressed? -!
    k=readKey()
    invLine(a,l)
 ENDWHILE
!- Clear Buffer -!
  k=None
END
I've tried to implement it in CLSN Pascal code.
It compiles but do nothing at all.
(*- Inverse string assembly sub --- *)
program inv;
const
  minR=0; maxR=23;
type
  linT = array[0..39] of byte;
var
  sMem: word absolute $58;
  _Adr: word absolute $c0;
  _Len: byte absolute $c2;
  linA: array[minR..maxR] of word;

(*- Initialize Line Screen Access - *)
procedure initVid;
var
  j: byte;
begin
  for j:=minR to maxR do begin
    linA[j] := j*40+sMem;
  end;
end;

(*- Inverse line of screen bytes -- *)
procedure invLine(_Adr: word; _Len: byte);
begin
  inline($a0/$00/  (*         LDY #$00      *)
                   (* --------------------- *)
         $b1/_Adr/ (* LOOP    LDA (_Adr),Y  *)
         $49/$80/  (*         EOR #$80      *)
         $91/_Adr/ (*         STA (_Adr),Y  *)
                   (* --------------------- *)
         $c8/      (*         INY           *)
         $cc/_Len/ (*         CPY _Len      *)
         $d0/$f2); (*         BNE LOOP      *)
end;

var
  x,y: byte; c: char;
begin
  x := 0; y := 0;
  initVid;
  _Adr := linA[y]+x; _Len := 39;
  c := readkey;
  invLine(_Adr,_Len);
  c := readkey;
end.
Here you may find the attached disk image with my attempts :).
I've absolutely lost.
Does anyone can help me?
Many thanks in advance!
zen.

 

Share this post


Link to post
Share on other sites

Well I did a little tracing with Mac/65. It looks like it's getting hung up in the runtime initialization, endlessly calculating the heap for the 64K banks. Needs somebody better with the Altirra debugger, but it looks like the compiler runtime is broken.

  • Like 1

Share this post


Link to post
Share on other sites
Hello there, Alfred!


Thank you for fast reply.

Here is my further suggestions.


Some exerpt from CLSN manual:


The format of the INLINE statement is: inline(bI/b2/b3/ ... );

Where bl, b2 and b3 represent the machine language code.


These values can be either byte constants or identifiers.

An identifier will generate two bytes of code representing its absolute address.

If an identifier is preceded by a or a ">" then only one byte of code is generated.

Using a "<" will generate the low byte of the address while using ">" will generate the high byte of the address.



As I can remember absolute operator mostly used in two cases:


1. to change location of variable from internal var area to defined address:

like


var

sMem: word absolute $58;


This statement moves sMem variable to SAVMSC thus sMem represents SAVMSC data i.e. screen address.


2. to superpose two variables


This code works.

program A;

var

sMem: word absolute $58;

sAdr: word; sVal: ^byte absolute sAdr;


begin

writeln;

sAdr := sMem;

sVal^ := 65-32;

end.


Here sAdr lays in pascal var area and after initialization is equal to SAVMSC data.

sVal become superposed to sAdr and after initialization is equal to screen byte A value. Thus A letter placed to screen.


We can change sAdr and A letter will be placed to another screen place.


It seems that I can NOT use sVal (without caret mark) in Pascal code.

It seems that I just can NOT use sVal^ in ML code!!!


zen.

Share this post


Link to post
Share on other sites

Hi!

 

A few comments:

 

 

!- Wait for Any Key Pressed ---------!
FUNC readKey()
  CONST None=255
  BYTE ch=764,res
BEGIN
  WHILE ch=None DO res=ch ENDWHILE
  ch=None
END res

 

The above will not correctly return the pressed key most of the time, as you set "res" after checking for "ch".

 

And in Pascal code:

 

 

(*- Inverse line of screen bytes -- *)
procedure invLine(_Adr: word; _Len: byte);
begin
  inline($a0/$00/  (*         LDY #$00      *)
                   (* --------------------- *)
         $b1/_Adr/ (* LOOP    LDA (_Adr),Y  *)
         $49/$80/  (*         EOR #$80      *)
         $91/_Adr/ (*         STA (_Adr),Y  *)
                   (* --------------------- *)
         $c8/      (*         INY           *)
         $cc/_Len/ (*         CPY _Len      *)
         $d0/$f2); (*         BNE LOOP      *)
end;

 

That ML routine is not correctly assembled, "CPY ZP" is $C4 and the branch destination should be $F5:

 

 $A0/$00/  (*         LDY #$00      *)
 $B1/_Adr/ (* Loop    LDA (_Adr),Y  *)
 $49/$80/  (*         EOR #$80      *)
 $91/_Adr/ (*         STA (_Adr),Y  *)
 $C8/      (*         INY           *)
 $C4/_Len/ (*         CPY _Len      *)
 $D0/$F5   (*         BNE Loop      *)

 

I've absolutely lost.

Does anyone can help me?

Many thanks in advance!

 

zen.

Rest of the code, I don't know, as I have not tested, but a cursory look shows that both are not the same (missing last loop on Pascal).

Share this post


Link to post
Share on other sites

Hello, DMSC.

 

Glad to see another answer!

 

As far as it's concerned PL65 code I can imagine that in the case of waiting any key pressed it must be the PROC without returning res variable :).

I simply used some previously prepared FUNC to accomplish the task. It may be called as Wait for Esc etc.

 

In the case of assembly routine I clearly understand that it's my foult. But all of my another attempts was erroneous!

 

I just can not understand the way of communication of Pascal ML part with it's own.

 

They say nothing about strike Pascal typification in the case of using ML codes.

How to explain Pascal that var sAdr is really the pointer to byte value in assembly?

 

In PL65 there is a absolute rule that any ZP value is a pointer!

Thus I can access any RAM address. But for only programming purposes it's too low leveled!

 

I really love CLSN IDE! After all my LISP, PL65, CC8, BasicXE investigations... I noticed that it gives me some another freedom of programming ideas.

But it kills my programming manner :).

As I can understand Pascal's Pointers are - never tracked dynamic addresses which are/may be organized in HEAP and can NOT be calculated from Pascal itself.

 

And thank you for your explaination of my error. As you can see PL65 variable is NOT ZP variable but I've used Pascal's as ZP in ML.

 

I repared and no success...

zen

Edited by 576XE

Share this post


Link to post
Share on other sites

Hi, friends -

Recently Alfred said

 

Well I did a little tracing with Mac/65. It looks like it's getting hung up in the runtime initialization, endlessly calculating the heap for the 64K banks.

 

It may be really essential, because of this fact:

CLSN manual says that -

The value returned by the @ operator is a longint with the highest
byte representing the bank (0 = Main; 1 .. 4 = Banks) and the low word
representing the absolute memory location.
1-st byte - Bank
2-nd, 3-rd bytes - Address (reasonably in Atari little endians notation)
If i can understand it may be the reason of erroneus behavior of invLine procedure.
zen
P.S.
Just now I've found Pascal memory vision.
Location Area
----------------- ----------------
$00000-$0FFFF Main Memory
$14000-$18000 Bank #1
$24000-$28000 Bank #2
$34000-$38000 Bank #3
$44000-$48000 Bank #4
And how to work with this structure?
zen
Edited by 576XE

Share this post


Link to post
Share on other sites
Hello, Friends!
This CRAZY CLSN Pascal effectively blows up my brain!!!
Please look at the codes...
This code goes to cycle?!! and fills up 16k program stack of CLSN with Pascal's speed ignoring inversion...
program tst10;
var
  sMem: word absolute $58;
(* Z-Page variables *)
  sPtr: pointer absolute $ca; (*longint: $ca=LSB,$cb=MSB,$cc=BKB*)
  sAdr: word absolute $ca;    (* word: $ca=LSB,$cb=MSB *)
  lMar: byte absolute $cd;    (* byte *)
  sLen: byte absolute $ce;    (* byte *)
  ch: char;


procedure invByte(a: word; m: byte);
begin
  sAdr := a; lMar := m;
  inline($a4/lMar/  (* ldy lMar     *)
         $b1/sAdr/  (* lda (sAdr),Y *)
(* Intentionally Blanked *)
         $49/$80/   (* eor #$80     *)
         $91/sAdr); (* sta (sAdr),Y *)
(* Intentionally Blanked *)
end;


begin
  writeln('Sample');
  sPtr := memptr($00,sMem);
  invByte(sAdr,5);
  ch := readkey;
end.

post-20208-0-62323200-1550814505.png

 

This code successively supresses sycling but ignores inverting?!!
program tst11;
var
  sMem: word absolute $58;
(* Z-Page variables *)
  sPtr: pointer absolute $ca; (*longint: $ca=LSB,$cb=MSB,$cc=BKB*)
  sAdr: word absolute $ca;    (* word: $ca=LSB,$cb=MSB *)
  lMar: byte absolute $cd;    (* byte *)
  sLen: byte absolute $ce;    (* byte *)
  ch: char;


procedure invByte(a: word; m: byte);
begin
  sAdr := a; lMar := m;
  inline($a4/lMar/  (* ldy lMar     *)
         $b1/sAdr/  (* lda (sAdr),Y *)
(* Intentionally Blanked *)
         $49/$80/   (* eor #$80     *)
         $91/sAdr/  (* sta (sAdr),Y *)
         $60);      (* rts          *)
end;


begin
  writeln('Sample');
  sPtr := memptr($00,sMem);
  invByte(sAdr,5);
  ch := readkey;
end.

post-20208-0-33977100-1550814518.png

 

This code adds inverting but... it's NOT inverting at all!
program tst12;
var
  sMem: word absolute $58;
(* Z-Page variables *)
  sPtr: pointer absolute $ca; (*longint: $ca=LSB,$cb=MSB,$cc=BKB*)
  sAdr: word absolute $ca;    (* word: $ca=LSB,$cb=MSB *)
  lMar: byte absolute $cd;    (* byte *)
  sLen: byte absolute $ce;    (* byte *)
  ch: char;


procedure invByte(a: word; m: byte);
begin
  sAdr := a; lMar := m;
  inline($a4/lMar/  (* ldy lMar     *)
         $b1/sAdr/  (* lda (sAdr),Y *)
         $00/       (* brk          *)
         $49/$80/   (* eor #$80     *)
         $91/sAdr/  (* sta (sAdr),Y *)
         $60);      (* rts          *)
end;


begin
  writeln('Sample');
  sPtr := memptr($00,sMem);
  invByte(sAdr,5);
  ch := readkey;
end.

post-20208-0-00676800-1550814539.png

 

Where am I? I'm still lost!
Please help!
zen

Share this post


Link to post
Share on other sites

 

Hello, Friends!
This CRAZY CLSN Pascal effectively blows up my brain!!!
Please look at the codes...
This code goes to cycle?!! and fills up 16k program stack of CLSN with Pascal's speed ignoring inversion...
program tst10;
var
  sMem: word absolute $58;
(* Z-Page variables *)
  sPtr: pointer absolute $ca; (*longint: $ca=LSB,$cb=MSB,$cc=BKB*)
  sAdr: word absolute $ca;    (* word: $ca=LSB,$cb=MSB *)
  lMar: byte absolute $cd;    (* byte *)
  sLen: byte absolute $ce;    (* byte *)
  ch: char;


procedure invByte(a: word; m: byte);
begin
  sAdr := a; lMar := m;
  inline($a4/lMar/  (* ldy lMar     *)
         $b1/sAdr/  (* lda (sAdr),Y *)
(* Intentionally Blanked *)
         $49/$80/   (* eor #$80     *)
         $91/sAdr); (* sta (sAdr),Y *)
(* Intentionally Blanked *)
end;


begin
  writeln('Sample');
  sPtr := memptr($00,sMem);
  invByte(sAdr,5);
  ch := readkey;
end.

attachicon.gifpic10.png

 

This code successively supresses sycling but ignores inverting?!!
program tst11;
var
  sMem: word absolute $58;
(* Z-Page variables *)
  sPtr: pointer absolute $ca; (*longint: $ca=LSB,$cb=MSB,$cc=BKB*)
  sAdr: word absolute $ca;    (* word: $ca=LSB,$cb=MSB *)
  lMar: byte absolute $cd;    (* byte *)
  sLen: byte absolute $ce;    (* byte *)
  ch: char;


procedure invByte(a: word; m: byte);
begin
  sAdr := a; lMar := m;
  inline($a4/lMar/  (* ldy lMar     *)
         $b1/sAdr/  (* lda (sAdr),Y *)
(* Intentionally Blanked *)
         $49/$80/   (* eor #$80     *)
         $91/sAdr/  (* sta (sAdr),Y *)
         $60);      (* rts          *)
end;


begin
  writeln('Sample');
  sPtr := memptr($00,sMem);
  invByte(sAdr,5);
  ch := readkey;
end.

attachicon.gifpic11.png

 

This code adds inverting but... it's NOT inverting at all!
program tst12;
var
  sMem: word absolute $58;
(* Z-Page variables *)
  sPtr: pointer absolute $ca; (*longint: $ca=LSB,$cb=MSB,$cc=BKB*)
  sAdr: word absolute $ca;    (* word: $ca=LSB,$cb=MSB *)
  lMar: byte absolute $cd;    (* byte *)
  sLen: byte absolute $ce;    (* byte *)
  ch: char;


procedure invByte(a: word; m: byte);
begin
  sAdr := a; lMar := m;
  inline($a4/lMar/  (* ldy lMar     *)
         $b1/sAdr/  (* lda (sAdr),Y *)
         $00/       (* brk          *)
         $49/$80/   (* eor #$80     *)
         $91/sAdr/  (* sta (sAdr),Y *)
         $60);      (* rts          *)
end;


begin
  writeln('Sample');
  sPtr := memptr($00,sMem);
  invByte(sAdr,5);
  ch := readkey;
end.
Where am I? I'm still lost!
Please help!
zen

 

 

You are using EOR instead of ORA to force the highest bit on.

Share this post


Link to post
Share on other sites

Hello, baktra!

 

If I can remember ORA just sets a bit in a byte while EOR effectively flips it back and forth.

 

In my last post I just tried to pay attention on strange effects of using BRK and RTS in inline Pascal code.

 

zen

Share this post


Link to post
Share on other sites

Today is one of my best days.

I've found the reasons of Pascal's bad understanding of assembly.

 

At first while pointers in Assembler are two-bytes addresses with special addressing modes, pointers in Pascal are three-bytes values laying in memory in special order (LSB,MSB,BNK).

Thus Pascal is awaiting the third byte while working with pointers. When it takes third value from code it just jam all program and not only break PC but Stack too!

First picture shows Stack overflow situation.

When I addes BRK code after address fields Pascal calmed down. The RTS really needed in inline insertions and not for inline procedures to clearly end up insertion but it may work the same role as BRK!

 

Please look at the first working code of Pascal's screen access.

 

program invl;
type
  linT = array[0..39] of byte;
  ptrT = ^byte;
  adrT = word;
var
  sMem: word absolute $58;
  lPtr: ptrT absolute $ca; (*longint: $ca=LSB,$cb=MSB,$cc=BNK*)
  lAdr: adrT absolute $ca; (* word: $ca=LSB,$cb=MSB *)
  lMar: byte absolute $cd;
  ch: char;


begin
  writeln('Hello, World!'); writeln;
  lPtr := memptr($00,lAdr);
  lMar := 4; lAdr := sMem+lMar;


  inline($a0/$03/ (*         LDY #$03      *)
         $b1/lAdr/ (* LOOP    LDA (lAdr),Y  *)
         $00/      (*         BRK           *)
         $49/$80/  (*         EOR #$80      *)
         $91/lAdr/ (*         STA (lAdr),Y  *)
         $60);     (*         RTS           *)


  write('lMar=',lMar,' '); writeln('lAdr=',lAdr);
  ch := readkey;
end.
post-20208-0-12689700-1550906168.png

 

zen

Edited by 576XE

Share this post


Link to post
Share on other sites

Hello, baktra!

 

If I can remember ORA just sets a bit in a byte while EOR effectively flips it back and forth.

 

In my last post I just tried to pay attention on strange effects of using BRK and RTS in inline Pascal code.

 

zen

Oh, where was main brain. Back to the school of logical fucntions with me. Sorry for misguiding you.

Share this post


Link to post
Share on other sites

hi baktra,

 

It is a case of algorithm! No more.

 

I just wanted to flip value but you said about inversion only.

Where is an error? Just misunderstanding.

 

zen

Share this post


Link to post
Share on other sites

I find it hard to believe that adding the BRK hanged anything, unless maybe your offsets were off by 1. It can't be the pointer business because it's not running code created by the compiler, so there's none of that pseudo-register bank business. This is getting interesting, I think I'm going to take some time and look into what's going on. I hate it when compilers do work right.

  • Like 1

Share this post


Link to post
Share on other sites

BRK is really a two byte instruction, so the code will return (from the OS IRQ handler) to the operand of the EOR instruction rather than the instruction itself. Add a dummy byte after BRK to fix it.

  • Like 1

Share this post


Link to post
Share on other sites

Ok, so the problem appears to be a misunderstanding how CLSN parses variables. The initial code:

 

inline($a0/$00/ (* LDY #$00 *)
$b1/_Adr/ (* LOOP LDA (_Adr),Y *)
$49/$80/ (* EOR #$80 *)
$91/_Adr/ (* STA (_Adr),Y *)
$c8/ (* INY *)
$cc/_Len/ (* CPY _Len *)
$d0/$f2); (* BNE LOOP *)

 

This doesn't work because CLSN doesn't know you coded zero page addresses. So when I look in the debugger what I see is

 

B10500 instead of B1C0. I don't know what $0005 means, maybe it's an offset into the procedure. Anyway, if you change the assembler to be

 

$b1/$c0/ and

$cc/$c2/ and also fix the branch, it's wrong too

$d0/$f5/

 

then the program works correctly.

 

It's a pretty big problem that CLSN won't substitute the proper 16 bit address into inline assembler. I'll have to go read the manual again, maybe there's something we're missing. I also haven't had much luck getting CLSN to run under SpartaDos. I don't think Sparta likes having $D301 fiddled with during serial i/o.

 

Edit: The manual says using the '< character will make the compiler only insert the LSB of the value. It doesn't work, still inserts two bytes. So INLINE blocks aren't much use.

Edited by Alfred
  • Like 1

Share this post


Link to post
Share on other sites
Hello, Alfred!


You are right!


Pascal being too serious teacher forbids some Atari related actions.


FE Any pointer in CLSN Pascal sits in memory in 3 bytes! LSB, MSB and then BNK with the purpose to access all 13XE memory.


I'm not being a programmer at all can not understand how it treats these data.


I know some facts from manual.

One of them says that absolute Atari(thus 2 bytes) address treats as bnkNum<$addr> ie 3 bytes ! ie $2000=$00,$2000 !

Another one says that when data goes from stack, then the operation of getting the address of this local variable (ie @) returns the offset to THE data of this variable from current stack pointer!

It's not processor stack it's 16k CLSN program stack!!!


I mean that only Pascal while compiling prevents successful ML operations.


By the way, absolute addressing operations ie STA $2000,Y is unsuccessful too!


We just can not represent to Pascal data perfectly!


As far as it's concern < - It's not a big problem.

Even if we send 2-bytes datum with the purpose of obtain < byte, we are successfully get it's byte because of Little Endian Atari notation of addressing.

Our beloved Accu. gets only one LSB byte!

While we are retreaving > byte we get 2 bytes value and then get the second datum from them.


PS And...

Many thanks from spring Moscow to you and family!

zen

Edited by 576XE

Share this post


Link to post
Share on other sites

Hello FRIENDS!

 

This code of CAS and LUCKYBUCK gently decides all problems :) !

 

program cas;
const
  Esc=#27;
var
  sMem : word absolute $58;


procedure invLine(x,y,len : byte);
var
  sAdr: integer absolute $f2;
  _l  : byte absolute $f0;
begin
  _l := len;
  sAdr := sMem + y*40 + x-1;
  inline($ac/_l/    (* ldy #_l     *)
         $b1/$f2/  (* lda($f2),y *)
         $49/$80/  (* eor #$80   *)
         $91/$f2/  (* sta($f2),y *)
         $88/      (* dey        *)
         $d0/$f5); (* bne -9     *)
end;


(*= Main Procedure =================*)
(*- Locals -------------------------*)
var
  _X,_Y,_L: byte; _C: char; 
begin
  writeln; writeln; writeln;
  _X := 0; _Y := 0; _L := 40;
  repeat
    _C := readkey;
    while _C='' do ; (* Wait... *)
    invLine(_X,_Y,_L);
  until _C=Esc;
end.

Simply ingenious.

zen

  • Like 1

Share this post


Link to post
Share on other sites

Hello FRIENDS!

 

This new code is working!

It's demo of course BUT ...

program vm;
const
  CON=0; COF=1;
  NOP=255; ESC=28; RET=12;
  UP=14; cUP=142; DN=15; cDN=143;
type
  sarT = array[1..6] of string;
var
  CH: byte absolute $2fc;
  SAVMSC: word absolute $58;
  CRSINH: byte absolute $2f0;
  X,Y,W,H: byte;


procedure invLine(x,y,len : byte);
var
  _L: byte absolute $f0;
  _A: integer absolute $f2;
begin
  _L := len;
  _A := SAVMSC + y*40 + x-1;
  inline($ac/_L/   (* ldy #_L    *)
         $b1/$f2/  (* lda($f2),y *)
         $49/$80/  (* eor #$80   *)
         $91/$f2/  (* sta($f2),y *)
         $88/      (* dey        *)
         $d0/$f5); (* bne -9     *)
end;


procedure putMenu;
var
  strg: sarT; ndx: byte;
begin
  strg[1] := 'Moscow    ';
  strg[2] := 'Petersburg';
  strg[3] := 'Evpatoria ';
  strg[4] := 'Feodosia  ';
  strg[5] := 'Simeiz    ';
  strg[6] := 'Konakovo  ';
  for ndx := 1 to H do begin
    gotoxy(X,Y-1+ndx); writeln(strg[ndx]);
  end;
end;


var
  MINR,MAXR,sel,key: byte;
  dy: shortint;
begin
  X := 5; Y := 5; W := 10; H := 6;
  MINR := 1; MAXR := 6;
  CRSINH := COF;
  putMenu;
  sel := 1;
  invLine(X,Y-1+sel,W);


  repeat
    CH := NOP;
    while CH=NOP do ;
    key := CH;
    dy := ord((key=DN) or (key=cDN)) - ord((key=UP) or (key=cUP));
    if dy<>0 then begin
      invLine(X,Y-1+sel,W);
      sel := sel+dy;
      if sel<MINR then sel := MINR;
      if sel>MAXR then sel := MAXR;
      invLine(X,Y-1+sel,W);
      CH := NOP;
    end;
    if key=RET then begin
      gotoxy(14,20); writeln(sel,' selected');
      CH := NOP;
    end;
  until key=ESC;
  CH := NOP;
  CRSINH := CON;
end.

And a screenshot:

 

post-20208-0-07644300-1552652300.png

 

zen

Edited by 576XE
  • Like 1

Share this post


Link to post
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.

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...
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...