Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 21:42 27 Nov 2024 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : PicoMite V5.07.08 betas

     Page 11 of 11    
Author Message
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 9125
Posted: 02:43pm 16 Sep 2023
Copy link to clipboard 
Print this post

It was just a trivial bug when I introduced the ability to merge the layer when saving.

Have a look at the PETSCII thread and the CSUB conversion thread. I could easily modify the blit memory command to take the encoded version which would reduce memory usage of the CSUB.

My program that creates the CSUB from an old style sprite file could do the additional encoding

Thoughts
 
disco4now

Guru

Joined: 18/12/2014
Location: Australia
Posts: 897
Posted: 06:41am 17 Sep 2023
Copy link to clipboard 
Print this post

  matherp said  
Thoughts

Hi Peter,
This is a Run Length Encoding method I played with at one time.

Its from this thread.
RLE image to SSD1963


* The image is stored using the following method.
* The 3 bytes used to define each RGB888 pixel are reduced to 2 bytes encoded
* as RGB565 ie. RRRRRGGG GGGBBBBB
* This is then stored using Run Length Encoding (RLE). Each pixel is stored as
* its two byte RGB565 code, unless a run of two or more identical pixel are detected.
* In this case only the first two of these identical pixel are written,
* a count is then made of the number of identical pixel beyond the first two.
* This count is then recorded using 1,2 or 3 bytes a detailed below.
*
* If count <128 then a single Byte is used ie.0XXX XXXX B7=0 and B(6)-B(0)
* contain the value.
* If count < 16384 then two bytes are used. Byte 0 has  B7=1 in to indicate
* an addition Byte is required. i.e.
* Byte 0 is 1XXX XXXX and Byte 1 has  B7=0 is 0XXX XXXX .This leaves 14 bits
* to store the counter.
* For a counter of > 16383 then three bytes are used. B7=1 for  Byte 1 indicating
* another byte is required. ie. 1XXX XXXX     1XXX XXXX   XXXX XXXX    
* All eight bits of Byte 2 are used to store the value, giving a total of 22 bits.
*
* This method of encoding is very effective when there are runs of the same pixel.
* e.g. A full screen of the same colour is stored in 7 bytes.

It can result in a bigger size if there are mostly runs of two pixels. In the CSUB header a bit could be used to indicate there is no RLE in these cases.


This is the source of a CSUB I was working on the show icons from a CSUB. It is called with the address of the CSUB and the icon number to get. It extracts the RLE info and writes to screen. An enhancement I started on was to nominate 1 or 2 background colours which could be replaced when the icon is loaded. So the same icon could be loaded with say a red, yellow or Green backgound.

If we could define a standard for these compressed icon,bitmaps sprites then the BLIT commands could be made to understand them on all the platforms and even use a CSUB to show them on the Micromites.

I have progressed an editor in Purebasic that allows the editing/creation of the icons, even allows import of bmp files and generates the CSUB. I have not looked at it for sometime though.





/*******************************************************************************
*
* MMBasic CSUB ShowIcon - Restores Run Length Encoded (RLE) image from
* a CSUB in Program Memory onto  SSD1963 LCD
* acknowledgement to Peter Mather for various CFunctions for SSD1963
* and flash chips which have be used to provide many of the routines used.

* When Generating the CSUB, use MERGE CSUB mode, and name the CSUB
* RestoreImageRLE
*
* Entry point is function
* void main(long long *x, long long *y, long long *width, long long *height,
* long long *address,long long *cspin )
*
* x,y,width and height defined the area of interest. 0,0,800,480 for the full screen
* the width and height MUST match those used when storing the image, however x and y
* can be different so its restored in a different position.The area must be with the
* screen boundaries.
* address is the page number to start reading from the flash chip.
* cspin is the chip select pin allocated for the flash chip. (F_CS) 58 on an E100
*
* Gerry Allardice 2020-2021
* V1.0 2020-12-30 Initial version for SSD1963
* V1.1 2021-01-25  Added OTM8009A 16bit mode  

******************************************************************************/
//#define debugging       // comment out on final version

#define Version 100     //Version 1.0
#include <stdarg.h>
#include <xc.h>   // Required for SFR defs
#include <sys/attribs.h>   // Required to use __longramfunc__
#include "../CFunctions.h"

/***** Uncomment the relevant line to compile for particular LCD******/
//#define SSD1963
//#define OTM8009A
#define ILI9341P8
//#define ILI9341P16_64      '16bit on 64 pin port  B9-B2 + E7-E0
//#define ILI9341P16_100     '16bit on 100 pin port A7-A0 + E7-E0
//#define ILI9341_SPI1_MX170
//#define ILI9341_SPI2_MX470
//#define ILI9488_SPI1_MX170
//#define ILI9488_SPI2_MX470
//#define ST7789_SPI1_MX170
//#define ST7789_SPI2_MX470
/*******************************************************************/

#ifdef SSD1963
//#define PINS64 //E64 driver Comment out for compilation for 100-pin part
 #define PINS100 //E100 driver Comment out for compilation for 64-pin part
 #define BITS8
 //#define BITS16
#endif
#ifdef ILI9341P8
 void ShowIcon_for_ILI9341_8Bit_MX470(){
    }
#define ILI9341
#define MX470
#define PINS64 //E64 driver port E7-E0
#define BITS8
#endif
#ifdef ILI9341P16_64
 void ShowIcon_for_ILI9341_16Bit_64Pin(){
    }
#define ILI9341
#define MX470
#define PINS64 //E64 driver Comment out for compilation for 100-pin part
 //#define PINS100 //E100 driver Comment out for compilation for 64-pin part
 //#define BITS8
 #define BITS16
#endif  
#ifdef OTM8009A
 #define PINS64 //E64 driver Comment out for compilation for 100-pin part
 //#define PINS100 //E100 driver Comment out for compilation for 64-pin part
 //#define BITS8   //8 bits on MCU and 74HC573 for bits 8-15 on LCD
                   //clock bits 8-15 into a latch using RD pin
 #define BITS16    //16 bits on MCU  bits 7-0 are E7-E0 on 100 and 64 pin
                   //                bit 15-8 are A7-A0 on 100 pin
                   //                bit 15-8 are B9-B2 on 64 pin
#endif
#ifdef ILI9341_SPI1_MX170
  void ShowIcon_for_ILI9341_SPI1_MX170(){
    }
  #define ILI9341SPI
  #define SPI2BYTE
  #define SPI
  #define MX170
#endif
#ifdef ILI9341_SPI2_MX470
   void ShowIcon_for_ILI9341_SPI2_MX470(){
    }
  #define ILI9341SPI
  #define SPI2BYTE
  #define SPI
  #define MX470
#endif
#ifdef ILI9488_SPI1_MX170
  void ShowIcon_for_ILI9488_SPI1_MX170(){
    }
  #define ILI9488SPI
  #define SPI3BYTE
  #define SPI
  #define MX170
#endif
#ifdef ILI9488_SPI2_MX470
  void ShowIcon_for_ILI9488_SPI2_MX470(){
    }
  #define ILI9488SPI
  #define SPI3BYTE
  #define SPI
  #define MX470
#endif
#ifdef ST7789_SPI1_MX170
  void ShowIcon_for_ST7789_SPI1_MX170(){
    }
  #define ST7789
  #define SPI2BYTE
  #define SPI
  #define MX170
#endif
#ifdef ST7789_SPI2_MX470
  void ShowIcon_for_ST7789_SPI2_MX470(){
    }
  #define ST7789
  #define SPI2BYTE
  #define SPI
  #define MX470
#endif
 


#define atrisinv *(volatile unsigned int *)(0xbf88601C) //latch registers
#define aread *(volatile unsigned int *)(0xbf886020) //latch registers
#define aport *(volatile unsigned int *)(0xbf886030)    //latch register LATA
#define aclrport *(volatile unsigned int *)(0xbf886034) //CLR for LATA
#define asetport *(volatile unsigned int *)(0xbf886038) //SET for LATA

#define btrisinv *(volatile unsigned int *)(0xbf88611C) //latch registers
#define bread *(volatile unsigned int *)(0xbf886120) //latch registers
#define bport *(volatile unsigned int *)(0xbf886130)    //latch register LATB
#define bclrport *(volatile unsigned int *)(0xbf886134) //CLR for LATB
#define bsetport *(volatile unsigned int *)(0xbf886138) //SET for LATB

#define ctrisinv *(volatile unsigned int *)(0xbf88621C) //latch registers  
#define cread *(volatile unsigned int *)(0xbf886220) //latch registers
#define cport *(volatile unsigned int *)(0xbf886230)    //latch register LATC
#define cclrport *(volatile unsigned int *)(0xbf886234) //CLR for LATC
#define csetport *(volatile unsigned int *)(0xbf886238) //SET for LATC

#define etrisinv *(volatile unsigned int *)(0xbf88641C) //latch registers
#define eread *(volatile unsigned int *)(0xbf886420) //latch registers
#define eport *(volatile unsigned int *)(0xbf886430)    //latch register LATE
#define eclrport *(volatile unsigned int *)(0xbf886434) //CLR for LATE
#define esetport *(volatile unsigned int *)(0xbf886438) //SET for LATE

#define DEVID (*(volatile unsigned int *)0xBF80F220)

#define TFT_CASET 0x2A
#define TFT_RASET 0x2B
#define TFT_RAMWR 0x2C
#define TFT_RAMRD 0x2E
#define TFT_MADCTL 0x36
#define TFT_MAD_MY 0x80
#define TFT_MAD_MX 0x40
#define TFT_MAD_MV 0x20
#define TFT_MAD_ML 0x10
#define TFT_MAD_RGB 0x00
#define TFT_MAD_BGR 0x08
#define TFT_MAD_MH 0x04
#define TFT_MAD_SS 0x02
#define TFT_MAD_GS 0x01
#define TFT_IDXRD 0x00 // ILI9341 only, indexed control register read

#ifdef SPI
#ifdef MX170

  #define SPICON *(volatile unsigned int *)(0xbf805800) //SPI1 status register
  #define SPISTAT *(volatile unsigned int *)(0xbf805810) //SPI1 status register
  #define SPIBUF *(volatile unsigned int *)(0xbf805820) //SPI1 output buffer
  #define SPIBRG *(volatile unsigned int *)(0xbf805830) //SPI1 output buffer
  #define SPICON2 *(volatile unsigned int *)(0xbf805840) //SPI1 status register
  #define SPISTATCLR *(volatile unsigned int *)(0xbf805814) //SPI1 status clear register

#endif
#ifdef MX470

  #define SPICON *(volatile unsigned int *)(0xbf805A00)               //SPI2 control register #1
  #define SPISTAT *(volatile unsigned int *)(0xbf805A10)              //SPI2 status register
  #define SPIBUF *(volatile unsigned int *)(0xbf805A20)               //SPI2 output buffer
  #define SPIBRG *(volatile unsigned int *)(0xbf805A30)               //SPI2 speed register
  #define SPICON2 *(volatile unsigned int *)(0xbf805A40)              //SPI2 control register #2
  #define SPISTATCLR *(volatile unsigned int *)(0xbf805A14)           //SPI1 status clear register
 
#endif
 
 //#define ILImode 0x018520  //SPICON SPI MODE ?
 #define ILImode 0x018260   //SPICON Mode 0   CKE B(8)=0 CKP B(6)=1
//#define ILImode 0x008220  //SPICON Mode 1  CKE B(8)=0 CKP B(6)=0
//#define ILImode 0x018360  //SPICON Mode 2  CKE B(8)=1 CKP B(6)=1
//#define ILImode 0x018320  //SPICON Mode 3  CKE B(8)=1 CKP B(6)=0  
//#define ILImode   0x8060  //SPICON
#define SPIBRGValue 0x0
#define SPIsend(a) {int j;SPIBUF=a; while((SPISTAT & 0x80)==0); j=SPIBUF;}  
#define SPIqueue(a) {while(SPISTAT & 0x02){};SPIBUF=a;}  

#endif




#ifdef SKIP  

void spi_write_command(unsigned char data){
PinSetBit(Option->LCD_CD, LATCLR);
PinSetBit(Option->LCD_CS, LATCLR);
SPIsend(data);
PinSetBit(Option->LCD_CS, LATSET);
}
void spi_write_cd(unsigned char command, int data, ...){
int i;
va_list ap;
va_start(ap, data);
spi_write_command(command);
for(i = 0; i < data; i++) spi_write_data((char)va_arg(ap, int));
va_end(ap);
}

#endif
#if defined ILI9341SPI || defined ILI9488SPI
//ILI9341 and ILI9488
void defineregion(long x, long y, long width,long height){
   long x1=x,x2=x+width-1,y1=y,y2=y+height-1;    
   
   //PinSetBit(Option->LCD_CS, LATCLR);
   PinSetBit(Option->LCD_CD, LATCLR);
   SPIsend(TFT_CASET);
   PinSetBit(Option->LCD_CD, LATSET);
   SPIsend(x1 >> 8);
   SPIsend(x1);
   SPIsend(x2 >> 8);
   SPIsend(x2);
   PinSetBit(Option->LCD_CD, LATCLR);
   SPIsend(TFT_RASET);
   PinSetBit(Option->LCD_CD, LATSET);
   SPIsend(y1 >> 8);
   SPIsend(y1);
   SPIsend(y2 >> 8);
   SPIsend(y2);
   PinSetBit(Option->LCD_CD, LATCLR);
   SPIsend(TFT_RAMWR);
   PinSetBit(Option->LCD_CD, LATSET);                          //set CD high
  // PinSetBit(Option->LCD_CS, LATSET);
}
#endif
#if defined ST7789
void defineregion(int xstart, int ystart, int width, int height) {
   int xend=xstart+width-1, yend=ystart+height-1;
   if(Option->DISPLAY_ORIENTATION==2){
       ystart+=80;
       yend+=80;
   }
   if(Option->DISPLAY_ORIENTATION==1){
       xstart+=80;
       xend+=80;
   }
   PinSetBit(Option->LCD_CD, LATCLR);
   //PinSetBit(Option->LCD_CS, LATCLR);
   //uSec(1000);
   SPIsend(TFT_CASET);
   PinSetBit(Option->LCD_CD, LATSET);
   uSec(1000);
   SPIsend(xstart >> 8);
   SPIsend(xstart);
   SPIsend(xend >> 8);
   SPIsend(xend);
   PinSetBit(Option->LCD_CD, LATCLR);
   //uSec(1000);
   SPIsend(TFT_RASET);
   PinSetBit(Option->LCD_CD, LATSET);
   uSec(1000);
   SPIsend(ystart >> 8);
   SPIsend(ystart);
   SPIsend(yend >> 8);
   SPIsend(yend);
   PinSetBit(Option->LCD_CD, LATCLR);
   // uSec(1000);
   SPIsend(TFT_RAMWR);
   //PinSetBit(Option->LCD_CS, LATSET);
   
}

#endif  
//Offsets into the persistent RAM of variables
#ifdef PINS64
   #define RS_Pin_No 27
   #define WR_Pin_No 24
   #define RS_Pin 0x1000   //bit 13 mask
   #define WR_Pin 0x0800   //bit 11 mask
   #define clrport bclrport
   #define setport bsetport
   #define port eport
   #define read eread
   #define trisinv etrisinv
  #ifdef ILI9341
   
   #define RSLo    {clrport=RS_Pin;}
   #define RSHi    {setport=RS_Pin;}
   #define WRLo    {clrport=WR_Pin;}
   #define WRHi    {setport=WR_Pin;}
   #define RDLo    (*(volatile unsigned int *)RDclrport)=RDpin
   #define RDHi    (*(volatile unsigned int *)RDsetport)=RDpin
 #endif
 #ifdef SSD1963
    void ShowIcon_for_SSD1963_8bit(){
    }
   #define RSLo    {clrport=RS_Pin;}
   #define RSHi    {setport=RS_Pin;}
   #define WRLo    {clrport=WR_Pin;}
   #define WRHi    {setport=WR_Pin;}
   #define RDLo    (*(volatile unsigned int *)RDclrport)=RDpin
   #define RDHi    (*(volatile unsigned int *)RDsetport)=RDpin
 #endif
 #ifdef OTM8009A
    void ShowIcon_for_OTM8009A_16bit(){
    }
   #define RD_Pin 0x8000    //b15 E64  
   #define RSLo    bclrport=RS_Pin
   #define RSHi    bsetport=RS_Pin

   #define WRLo    bclrport=WR_Pin
   #define WRHi    bsetport=WR_Pin
   #define RDLo    bclrport=RD_Pin
   #define RDHi    bsetport=RD_Pin

   #define CLKLo    bclrport=RD_Pin;
   #define CLKHi    bsetport=RD_Pin;
 
   #define WRTog   WRLo; n=DEVID; WRHi;
 #endif
#endif
#ifdef PINS100
  //E100 Board
   #define F_CS 58         // flashchip CS
   #define RS_Pin_No 18    //RE8
   #define WR_Pin_No 19    //RE9
   #define RD_Pin_No 6     // Pin 6/RC1 E100//if not used tie to 3.3v  //RB15 pIN 30
   #define RS_Pin 0x0100    //b8 portE
   #define WR_Pin 0x0200    //b9 portE
   #define RD_Pin 0x0002    //b1 portc

   #define clrport bclrport
   #define setport bsetport
   #define port eport
   #define read eread  
   
   #define trisinv etrisinv
 
 
   #define RSLo    eclrport=RS_Pin
   #define RSHi    esetport=RS_Pin
   #define WRLo    eclrport=WR_Pin
   #define WRHi    esetport=WR_Pin
   #define RDLo    cclrport=RD_Pin
   #define RDHi    csetport=RD_Pin
  // #define RDTog   RDLo;n=DEVID;n=DEVID;n=DEVID;n=DEVID;n=DEVID;n=DEVID;RDHi;
   #define RDTog   RDLo;n=DEVID;n=DEVID;RDHi;
   #define WRTog   WRLo;WRHi;
   #define WRTog3  WRLo;WRHi;WRLo;WRHi;WRLo;WRHi;

  // #define CLKLo   bclrport=RD_Pin;
  // #define CLKHi   bsetport=RD_Pin;
  // #define WRTog   WRLo; n=DEVID; WRHi;
  // #define WRTog   WRLo; n=DEVID;n=DEVID;WRHi;


#endif

#define Both WR_Pin | RS_Pin


#define LANDSCAPE       1
#define PORTRAIT        2
#define RLANDSCAPE      3
#define RPORTRAIT       4



#ifdef debugging

//Function that gets our address offset so we dont need to pass it in.

__attribute__((noinline)) void getFPC(void *a, void *b, volatile unsigned int *c)
    {
        *c = (unsigned int) (__builtin_return_address (0) - (b -a)) ;
    }

void pstring(const char *s){
   volatile unsigned int libAddr ;
   getFPC(NULL,&&getFPCLab,&libAddr) ; // warning can be ignored, stupid editor
   getFPCLab: { }
   unsigned char  * testData    = (unsigned char *)((void *)s + libAddr );
   MMPrintString(testData);
}

void p_int(int a,int base){
   char b[64];
   IntToStr(b,a,base);
   MMPrintString(b);
}
void p_float(float a,int m, int n, char c){
// m is the nbr of chars before the decimal point
// n is the nbr chars after the point
// ch is the leading pad char
   char b[14];
   FloatToStr(b,a, m, n ,c);
   MMPrintString(b);
}
#endif
void p_int(int a,int base){
   char b[10];
   IntToStr(b,a,base);
   MMPrintString(b);
   putConsole(32);
}
#ifdef OTM8009A

/*******************************************************************************
*
* Write Data to a register on the OTM8009A Chip
*
******************************************************************************/


void write_command_data16(unsigned int command, int data, ...){
   long n;
   int i;
   int nextdata;
 
 
 
#ifdef BITS8
  CLKHi;  //set CLK Hi to load latch
  RSLo;  // i.e a command

  va_list ap;
  va_start(ap, data);
  //load bits 8-15
  eclrport=(0xFF);    // B0-B7
  esetport=((command>>8)  & 0xFF) ; //bits B15-B8 into B7-b0
  CLKLo;  //set CLK Lo to lock latch
  //B7-B0 in PORT E
  eclrport=(0xFF);    // B0-B7
  esetport=(command  & 0xFF) ; //bits B7-B0 into B7-b0
  WRTog;  //  RS low so send command
 
  CLKHi;  //set CLK Hi to load latch  
  RSHi;  //ie data
  for(i = 0; i < data; i++) {
      nextdata=(char)va_arg(ap, int); //reads data in increments pointer
      CLKHi;  //set CLK Hi to load latch
      eclrport=(0xFF);
      esetport=((nextdata>>8)  & 0xFF) ; //bits B15-B8 into B7-b0
      CLKLo;  //set CLK Lo to lock latch
      eclrport=(0xFF);
      esetport=(nextdata & 0xFF) ;
      WRTog; //  RS high so send data
  }
  va_end(ap);
  CLKHi;  //set CLK Hi to load latch
#endif  
 
#ifdef BITS16
 
  RDHi;  //set RD Hi if not tied to 3.3V
  RSLo;  // i.e a command

  va_list ap;
  va_start(ap, data);
 
  //B7-B0 in PORT E
  eclrport=(0xFF);    // B0-B7
  esetport=(command  & 0xFF) ; //bits B7-B0 into B7-b0
  // B15-B8 in PORT C
  bclrport=(0x3FC);     // B8-B15
  bsetport=((command >> 6)  & 0x3FC) ; //B15-8 moved to B9-B2 for B15-B8
  WRTog;// WRLo;    WRHi; // RS low
  RSHi;  //ie data
  for(i = 0; i < data; i++) {
      nextdata=(char)va_arg(ap, int); //reads data in increments pointer
      eclrport=(0xFF);
      esetport=(nextdata & 0xFF) ;
      bclrport=(0x3FC);     // B8-B15
      bsetport=((nextdata >> 6)  & 0x3FC)  ;
      WRTog;
               
  }
  va_end(ap);  
#endif

}

/*******************************************************************************
*
* defines start/end coordinates for memory access from host to OTM8009A
* also maps the start and end points to suit the orientation
*
* This function is a modified version of the function inside the MMBasic Interpreter
* for MM+ on 'MX470 chips
*
*******************************************************************************/
void defineregion(long x, long y, long width,long height){ //ILI9325
   long x1=x,x2=x+width-1,y1=y,y2=y+height-1;
   unsigned long xstart,xend,ystart,yend,Vertical,Horizontal; //xp,yp,
   if(HRes<VRes){
       Vertical=VRes;
       Horizontal=HRes;
   }
   else {
       Vertical=HRes;
       Horizontal=VRes;
   }
 
   xstart = x1;
   xend = x2;
   ystart = y1;
   yend = y2;
 
   write_command_data16(0x2A00,1,xstart>>8);
   write_command_data16(0x2A01,1,xstart & 0xFF);
   write_command_data16(0x2A02,1,xend>>8);
   write_command_data16(0x2A03,1,xend & 0xFF);
   write_command_data16(0x2B00,1,ystart>>8);
   write_command_data16(0x2B01,1,ystart & 0xFF);
   write_command_data16(0x2B02,1,yend>>8);
   write_command_data16(0x2B03,1,yend & 0xFF);

}


#endif
#ifdef ILI9341P8
/*******************************************************************************
*
* Write Data to a register on the Chip
*
******************************************************************************/
void write_command_data(unsigned int command, int data, ...){
int i;
#ifdef MX470
//RDHi; //set RD Hi if not tied to 3.3V
RSLo;
#endif
va_list ap;
va_start(ap, data);
port=(command & 0x00FF) | WR_Pin; WRLo; WRHi; // RS low
#ifdef MX470
RSHi;
#endif
for(i = 0; i < data; i++) {
port= (char)va_arg(ap, int) | Both; WRLo; WRHi; //RS high
}
va_end(ap);
}


/*******************************************************************************
*
* defines start/end coordinates for memory access from host to ILI9341
* also maps the start and end points to suit the orientation
*******************************************************************************/
void defineregion(long x, long y, long width,long height){ //ILI9341
long x1=x,x2=x+width-1,y1=y,y2=y+height-1;
unsigned long xstart,xend,ystart,yend,Vertical,Horizontal; //xp,yp,
if(HRes<VRes){
Vertical=VRes;
Horizontal=HRes;
}
else {
Vertical=HRes;
Horizontal=VRes;
}

xstart = x1;
xend = x2;
ystart = y1;
yend = y2;


write_command_data(0x2A,4,xstart>>8,xstart,xend>>8,xend);
write_command_data(0x2B,4,ystart>>8,ystart,yend>>8,yend);


}

#endif
#ifdef SSD1963
/*******************************************************************************
*
* Write Data to a register on the Chip 8bit SSD1963
*
******************************************************************************/
void write_command_data(unsigned int command, int data, ...){
  int i;
  RSLo;
  va_list ap;
  va_start(ap, data);
  port=(command & 0x00FF) | WR_Pin;  WRLo;    WRHi; // RS low
  RSHi;
  for(i = 0; i < data; i++) {
      port= (char)va_arg(ap, int) | Both;  WRLo;    WRHi; //RS high
  }
  va_end(ap);
}
/*******************************************************************************
*
* defines start/end coordinates for memory access from host to SSD1963
* also maps the start and end points to suit the orientation
*
*******************************************************************************/
void defineregion(long x, long y, long width,long height){ //SSD1963
   long x1=x,x2=x+width-1,y1=y,y2=y+height-1;
   unsigned long xstart,xend,ystart,yend,Vertical,Horizontal;

       RSLo;

       if(HRes>VRes){
       Vertical=VRes;
       Horizontal=HRes;
   }
   else {
       Vertical=HRes;
       Horizontal=VRes;
   }
    if(Option->DISPLAY_ORIENTATION!=LANDSCAPE)goto isP;
             xstart = x1;
             xend = x2;
             ystart = y1;
             yend = y2;
             if(Option->LCD_CD>6){ //reverse for 7" displays
               xstart = (Horizontal - 1) - x2;
               xend = (Horizontal - 1) - x1;
             }
             goto setreg;
    isP:        
    if(Option->DISPLAY_ORIENTATION!=PORTRAIT)goto isRL;
             xstart = y1;
             xend = y2;
             ystart = (Vertical - 1) - x2;
             yend = (Vertical - 1) - x1;
             goto setreg;
   isRL:
   if(Option->DISPLAY_ORIENTATION!=RLANDSCAPE)goto isRP;
             xstart = (Horizontal - 1) - x2;
             xend = (Horizontal - 1) - x1;
             ystart = (Vertical - 1) - y2;
             yend = (Vertical - 1) - y1;
             if(Option->LCD_CD>6){//reverse for 7" displays
               xstart = x1;
               xend = x2;
             }
             goto setreg;
    isRP:
             xstart = (Horizontal - 1) - y2;
             xend = (Horizontal - 1) - y1;
             ystart = x1;
             yend = x2;
   setreg:
   port=0x22A ;WRLo;    WRHi; // RS low
 
       RSHi;
   
   port=(xstart>>8 ) | Both;  WRLo;    WRHi; // RS HIGH
   port=(xstart) | Both;  WRLo;    WRHi; // RS HIGH
   port=(xend>>8 ) | Both;  WRLo;    WRHi; // RS HIGH
   port=(xend) | Both;  WRLo;    WRHi; // RS HIGH
 
       RSLo;
 
   port=0x22B ;  WRLo;    WRHi; // RS low
   
       RSHi;
 
   port=(ystart>>8 ) | Both;  WRLo;    WRHi; // RS HIGH
   port=(ystart) | Both;  WRLo;    WRHi; // RS HIGH
   port=(yend>>8 ) | Both;  WRLo;    WRHi; // RS HIGH
   port=(yend) | Both;  WRLo;    WRHi; // RS HIGH    RSHi;
}
#endif

/*******************************************************************************
*CSUB ShowIcon - Restores RLE RGB565 image stored in program flash
*as a CSUB and displays data on LCD screen SSD1963,OTM8009A
* Usage:
* ShowIcon x,y,iconno,addressofcsub,returncode
* *****************************************************************************/

void main(long long *x, long long  *y, long long *iconnumber, long long *iconaddr,long long *returncode){
   long n;   //used in WRTog  DONT USE HERE for anything else
   // unsigned long bg1=*alt1,bg2=*alt2;
   // p_int(bg1,16);
   // p_int(bg2,16);
   // int t,m;
  // int add;//pin=*returncode;
   unsigned int iconno=*iconnumber,iconmax=0,iconstart=0;
  // add=*address<<8; //convert page number to byte number
   int x1=*x,y1=*y;
  // int width=*w,height=*h;
   int width=32,height=32;
  // unsigned int brgsave=0;
   unsigned long ch,cm,cl,i;
   unsigned long pixel1=0,pixel2=0,firstpixel=0;//,rle=0;
   unsigned long pcounter=0,pcounter1=0,pcounter2=0,pcounter3=0;
 
   int p;
   int pf=*iconaddr;
   
   //unsigned int iret;
   unsigned char iret;
   //char iret;
   
   *returncode=0;
  #ifdef SPI  
   unsigned int consave=0,brgsave=0,con2save;
   //save any user SPI setup
   brgsave=SPIBRG;
   consave=SPICON;
   con2save=SPICON2;
   // switch to SPI enhanced mode for the bulk transfer
   SPICON=0x0;  //Stops and resets the SPI
   //SPICON=ILImode;
   //SPICON=0x018260;
   SPICON = consave | (1 << 16);
   SPIBRG=0;      //Set clock divider
   // SPIBRG=1;   //Set clock divider 0x1 is /4
   SPICON2=0xC00;
   PinSetBit(Option->LCD_CS, LATCLR);
  #endif
/* Lets read in the CSUB RLE file and extract the pixels*/  
 
     i=0;
     p=0;
     // byte 0 is not used yet maybe a check byte in future.
     iret = *((char *)(pf+p));
      p++;
     // byte 1 is the max number of icons. Over 255 will break it
     iret = *((char *)(pf+p));
     p++;
     iconmax=iconmax | iret;
     
   if ((iconno+1)<=iconmax){
   
     //Now find where the offset to where our icon starts is stored
     //The start address is high byte at 2+iconno*2
     p=2+(iconno*2);
     iret = *((char *)(pf+p));
     p++;
     iconstart=iret & 0xff ;
     iconstart=iconstart<<8;
     iret = *((char *)(pf+p));
     p++;
    //  p_int(iret,16);
     iconstart=iconstart | iret; //add the low byte
    // p_int(iconstart,16);  
    //; putConsole(10);
    // putConsole(13);
     
     //The first two bytes are width and height for the icon
     p=iconstart;
     iret = *((char *)(pf+p));
     p++;
     width=(iret & 0xFF);
     iret = *((char *)(pf+p));
     p++;
     height=(iret & 0xFF);
    // p_int(width,16);
    // p_int(height,16);
    // p_int(width,10);
    // p_int(height,10);
    // putConsole(13);
   
 
           
  // if(x1 < 0) x1 = 0; if(x1 >= HRes) x1 = HRes - 1;
  // if(width < 0) width = 0; if(width > (HRes-x1)) width = HRes - x1;
  // if(y1 < 0) y1 = 0; if(y1 >= VRes) y1 = VRes - 1;
 //  if(height < 0) height = 0; if(height > (VRes-y1)) height = VRes - y1;
   
   /* Move inside screen boundaries if it doesnt fit already*/
   if(x1 < 0) x1 = 0; if(x1+width >= HRes) x1 = HRes -width;
   if(y1 < 0) y1 = 0; if(y1+height >= VRes) y1 = VRes -height;
     
   i=width*height;
 
  // p_int(i,10);
  // p_int(i,16);
  // putConsole(13);
   
   defineregion(x1,y1,width,height);
   
   #if defined SSD1963 ||  defined ILI9341
     RSLo;
     port=0x22C ;  WRLo;    WRHi; // RS low
     RSHi;
   #endif
   #ifdef OTM8009A
     write_command_data16(0x2C00,0);  //write to memory
     RSHi;  //ie data
     RDHi;  //set RD Hi if not tied to 3.3V
   #endif
   #ifdef SPI
    PinSetBit(Option->LCD_CD, LATSET);
    //PinSetBit(Option->LCD_CS, LATCLR);
    //SPIsend(data);
    //PinSetBit(Option->LCD_CS, LATSET);
   #endif
   
   
       
       while (i>0) {
           if (firstpixel==0){ //special case for first pixel
               //write to screen
               i--;
               firstpixel=1;
               ch = *((char *)(pf+p));
               p++;
               ch = ch & 0xFF ;  
               cl = *((char *)(pf+p));
                p++;
               cl = cl & 0xFF ;
               
               pixel1=(ch<<8)|cl;
             
               #ifdef SSD1963
                 cm = ((ch << 5 ) & 0b11100000) | ((cl >> 3) & 0b00011100);  
                 ch= (ch  & 0b11111000) ;
                 cl= ((cl << 3) & 0b11111000);  
                 port=ch | Both;    WRLo;    WRHi;
                 port=cm | Both;    WRLo;    WRHi;
                 port=cl | Both;    WRLo;    WRHi;
               #endif
               #ifdef ILI9341P8
                  port=ch;WRLo;WRHi;
                  port=cl;WRLo;WRHi;
               #endif
               #ifdef OTM8009A
                 bclrport=(0x3FC);
                 bsetport=(((ch<<2) & 0x3FC) );
                 eclrport=(0xFF);
                 esetport=(cl & 0xFF); //B0-B7 is RE0-RE7
                 WRTog;
               #endif
               #ifdef SPI2BYTE
                  SPIqueue(ch);SPIqueue(cl);
               #endif
               #ifdef SPI3BYTE
                 cm = ((ch << 5 ) & 0b11100000) | ((cl >> 3) & 0b00011100);
                 ch= (ch  & 0b11111000) ;
                 cl= ((cl << 3) & 0b11111000);  
                 SPIqueue(ch);SPIqueue(cm);SPIqueue(cl);
               #endif    
           }else{
                //subsequent pixels  
                ch = *((char *)(pf+p));
                p++;  
                ch = ch & 0xFF ;  
                cl = *((char *)(pf+p));
                p++;
                cl = cl & 0xFF ;
                pixel2=(ch<<8)|cl;
               
                  if (pixel1==pixel2) {
                     //read the RLE count and write the second pixel
                     //write repeated pixel to screen
                     i--;
                     #ifdef SSD1963
                        cm = ((ch << 5 ) & 0b11100000) | ((cl >> 3) & 0b00011100);  
                        ch= (ch  & 0b11111000) ;
                        cl= ((cl << 3) & 0b11111000);  
                        port=ch | Both;    WRLo;    WRHi;
                        port=cm | Both;    WRLo;    WRHi;
                        port=cl | Both;    WRLo;    WRHi;
                     #endif
                     #ifdef ILI9341P8
                        port=ch;WRLo;WRHi;
                        port=cl;WRLo;WRHi;
                     #endif
                     #ifdef OTM8009A
                       bclrport=(0x3FC);
                       bsetport=(((ch<<2) & 0x3FC) );
                       eclrport=(0xFF);
                       esetport=(cl & 0xFF); //B0-B7 is RE0-RE7
                       WRTog;
                     #endif
                     #ifdef SPI2BYTE
                          SPIqueue(ch);SPIqueue(cl);
                      #endif
                      #ifdef SPI3BYTE
                        cm = ((ch << 5 ) & 0b11100000) | ((cl >> 3) & 0b00011100);
                        ch= (ch  & 0b11111000) ;
                        cl= ((cl << 3) & 0b11111000);
                        SPIqueue(ch);SPIqueue(cm);SPIqueue(cl);
                      #endif        
                     // now read the RLE counter
                     pcounter1 = *((char *)(pf+p));
                      p++;
                     if (pcounter1<128){
                         pcounter=pcounter1;
                     }else{
                         pcounter1=(pcounter1 & 0b01111111);
                          pcounter2 = *((char *)(pf+p));
                          p++;
                         if (pcounter2<128){
                            pcounter=(pcounter1<<7) | pcounter2;
                         }else{
                            pcounter2=(pcounter2 & 0b01111111);
                            pcounter3 = *((char *)(pf+p));
                            p++;
                            pcounter=(pcounter1<<15) | (pcounter2 <<8) | pcounter3;
                         }  
                     }
                      //p_int(i,10);
                     // p_int(pcounter,10);
                     // putConsole(13);
                     while ( pcounter--) {
                        //write repeated pixel to screen
                       i--;  
                       #ifdef SSD1963
                         port=ch | Both;    WRLo;    WRHi;
                         port=cm | Both;    WRLo;    WRHi;
                         port=cl | Both;    WRLo;    WRHi;
                       #endif
                       #ifdef ILI9341P8
                          port=ch;WRLo;WRHi;
                          port=cl;WRLo;WRHi;
                       #endif
                       #ifdef OTM8009A
                         bclrport=(0x3FC);
                         bsetport=(((ch<<2) & 0x3FC) );
                         eclrport=(0xFF);
                         esetport=(cl & 0xFF); //B0-B7 is RE0-RE7
                         WRTog;
                       #endif
                       #ifdef SPI2BYTE
                          SPIqueue(ch);SPIqueue(cl);
                       #endif
                       #ifdef SPI3BYTE
                          SPIqueue(ch);SPIqueue(cm);SPIqueue(cl);
                       #endif      
                     }
                     //start new pixel
                     firstpixel=0;
                 
                }else{
                   
                   //just a non matching pixel, no RLE in play
                       //write to screen
                       i--;
                       #ifdef SSD1963
                         cm = ((ch << 5 ) & 0b11100000) | ((cl >> 3) & 0b00011100);  
                         ch= (ch  & 0b11111000) ;
                         cl= ((cl << 3) & 0b11111000);  
                         port=ch | Both;    WRLo;    WRHi;
                         port=cm | Both;    WRLo;    WRHi;
                         port=cl | Both;    WRLo;    WRHi;
                       #endif
                       #ifdef ILI9341P8
                          port=ch;WRLo;WRHi;
                          port=cl;WRLo;WRHi;
                       #endif
                       #ifdef OTM8009A
                         bclrport=(0x3FC);
                         bsetport=(((ch<<2) & 0x3FC) );
                         eclrport=(0xFF);
                         esetport=(cl & 0xFF); //B0-B7 is RE0-RE7
                         WRTog;
                       #endif
                       #ifdef SPI2BYTE
                          SPIqueue(ch);SPIqueue(cl);
                       #endif  
                       #ifdef SPI3BYTE
                          cm = ((ch << 5 ) & 0b11100000) | ((cl >> 3) & 0b00011100);  
                          ch= (ch  & 0b11111000) ;
                          cl= ((cl << 3) & 0b11111000);
                          SPIqueue(ch);SPIqueue(cm);SPIqueue(cl);
                       #endif    
                       pixel1=pixel2;
               }  
               
           }
         
       }
        #ifdef SPI
         
          while((SPISTAT & 0x80)==0); //wait for all writes to complete
          while(!(SPISTAT & 0x20)){i=SPIBUF;}   // clean up rx fifo if not empty
          SPICON=0x0;  //Stops and resets the SPI
          SPISTATCLR=0x40;
          // revert to previous SPI mode
          SPIBRG=brgsave;  //restore user (or my) setup
          SPICON=consave;
          SPICON2=con2save;
          PinSetBit(Option->LCD_CS, LATSET);
         
       #endif
    } else{
       *returncode=1;
    }
       
     
}







Latest F4 Latest H7
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 9125
Posted: 07:25am 17 Sep 2023
Copy link to clipboard 
Print this post

Thanks for that but the images we are encoding are 4bit only so I'm going for something really simple
each byte will be a nibble of count and then a nibble of colour. If the count is >15 then a second byte and possibly subsequent bytes for that colour will be needed.
Seems to work well and is pretty efficient
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 9125
Posted: 08:35am 17 Sep 2023
Copy link to clipboard 
Print this post

NB: Re-upload at 9:34 UTC 17/09 to improve performance of compressed blits

V5.07.08b17

https://geoffg.net/Downloads/picomite/PicoMite_Beta.zip

Implements

BLIT COMPRESSED address%, x%, y% [,col]

This Blits a compressed csub to the current framebuffer (or VGA screen) see here for how to create a compressed CSUB from a Maximite style sprite file

As before col is optional and specifies a colour that shouldn't be copied (-1 to 15) defaults to -1 if not specified indicating that all colours should be copied
Edited 2023-09-17 20:35 by matherp
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 4246
Posted: 11:00am 17 Sep 2023
Copy link to clipboard 
Print this post

Some observations on b16

- when using layers, error messages are only shown on serial port, not screen

- the inbuilt editor corrupt sprite files.
Maybe something with eol termination

Volhout
PicomiteVGA PETSCII ROBOTS
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 9125
Posted: 11:40am 17 Sep 2023
Copy link to clipboard 
Print this post

  Quote  - when using layers, error messages are only shown on serial port, not screen


Has always been the case but will consider switching to screen on error


  Quote   the inbuilt editor corrupt sprite files.


Files are tokenised when stored in memory and this may corrupt things which aren't Basic syntax. This is a limitation of the MMbasic editor and would need a big change to edit to and from disk
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 6798
Posted: 12:26pm 17 Sep 2023
Copy link to clipboard 
Print this post

The PicoMite family never ceases to amaze me.....  :)
Mick

Zilog Inside! nascom.info for Nascom & Gemini
Preliminary MMBasic docs & my PCB designs
 
vegipete

Guru

Joined: 29/01/2013
Location: Canada
Posts: 1109
Posted: 06:59pm 17 Sep 2023
Copy link to clipboard 
Print this post

Can these new versions of BLIT from flash include mirroring? (Left/Right and/or Up/Down.)
Visit Vegipete's *Mite Library for cool programs.
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 4246
Posted: 08:17pm 17 Sep 2023
Copy link to clipboard 
Print this post

Hi Peter,


option base 1
dim a$(64) length 128
memory


uses 9kbyte memory in 2 variables.... 2 ????


option base 1
dim a$(64,128) length 1
memory


uses 16kbyte memory in 2 variables.... 16kbyte ?

Any idea why 16 kbyte ?

I am looking into a way to store bytes in a 64x128 array. Technically 8kbyte.
The first option I can use MID$ to get the value.
Another option is to use a long string, have not investigated yet.

Volhout
Edited 2023-09-18 06:20 by Volhout
PicomiteVGA PETSCII ROBOTS
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 9125
Posted: 09:12pm 17 Sep 2023
Copy link to clipboard 
Print this post

  Quote  Can these new versions of BLIT from flash include mirroring? (Left/Right and/or Up/Down.)

No: they are designed for high speed. To mirror need massive amounts of nibble twiddling which is massively inefficient


64*129=8,256 rounds up to 9K
64*128*2=16284 = 16k

Every string has a length byte so a string length of 1 takes 2 bytes
Edited 2023-09-18 07:14 by matherp
 
carlschneider
Senior Member

Joined: 04/08/2023
Location: South Africa
Posts: 158
Posted: 11:17pm 17 Sep 2023
Copy link to clipboard 
Print this post

Hi Peter

Would it be possible to allow an option to increase the cut, copy and paste buffer size in the EDITor?

When doing development it would be nice to be able to cut and paste larger portions of code and then trim the buffer back to the smaller default when development is complete or memory space becomes challenging.
Cheers Carl                                                        
Retirement is tough on Hobbies without a day job
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 6798
Posted: 07:30am 18 Sep 2023
Copy link to clipboard 
Print this post

@ Volhout

If you get the start address of the string +1 (to skip the length byte) then add an offset to it it's quicker to peek and poke than to use MID$ for individual bytes or words, I think. If you are after a numeric value, that is.
Mick

Zilog Inside! nascom.info for Nascom & Gemini
Preliminary MMBasic docs & my PCB designs
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 9125
Posted: 03:59pm 18 Sep 2023
Copy link to clipboard 
Print this post

V5.07.08b18

https://geoffg.net/Downloads/picomite/PicoMite_Beta.zip

Fixes a bug in multi-line comments

When program errors occur and output is set to a framebuffer the firmware now automatically switches to the console so the error message is visible.

Implements EDIT fname$

If a filename is specified the editor will load the file from the current disk (A: or B:) to allow editing and on exit with F1 or F2 save it to the disk. If the file does not exist it is created on exit. The current program stored in flash memory is not affected. If editing an existing file a backup with .bak appended to the filename is also created on exit.
If fname$ includes an extension other than .bas then colour coding will be temporarily turned off during the edit.
If no extension is specified the firmware will assume .bas
Editing a file from disk allows non-Basic files such as html or sprite files to be edited without corruption during the tokenising process that happens when stored to flash.

NB: edit without a filename works exactly as before. The program is loaded from flash memory and saved to flash memory.
NB: On the PicoMiteVGA the edit command will automatically set to mode 1. The EDIT 1/2 version of the command is deprecated.

SO:
EDIT : from flash to flash
EDIT fname$ : from disk to disk

The edit buffer for cut and paste has been increased to 1024 characters but there is still a limitation that lines longer than the current display width cannot be cut and pasted.
 
carlschneider
Senior Member

Joined: 04/08/2023
Location: South Africa
Posts: 158
Posted: 04:08pm 18 Sep 2023
Copy link to clipboard 
Print this post

Thanks Peter

Was the multi line comment bug related to
/*
something
*/
/*
else
/*
Cheers Carl                                                        
Retirement is tough on Hobbies without a day job
 
Amnesie
Guru

Joined: 30/06/2020
Location: Germany
Posts: 396
Posted: 04:12pm 18 Sep 2023
Copy link to clipboard 
Print this post

Nice implementation,

but I guess with this amount of new things, it is a nightmare to keep the user manual up to date  

Greetings
Daniel
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 9125
Posted: 04:14pm 18 Sep 2023
Copy link to clipboard 
Print this post

  Quote  Was the multi line comment bug related to


It was related to any tokenisable code in the comment. Although the interpreter would skip everything in the comment, the tokeniser would get its knickers in a twist.
For example a subroutine name in the comment would error if the same name occurred outside the comment.
I hope now that anything in the comment is now ignored by the tokeniser and therefore won't cause problems

  Quote  but I guess with this amount of new things, it is a nightmare to keep the user manual up to dat


The manual is only updated at each full release. In between you have to scan this sort of thread for anything new or changed
Edited 2023-09-19 02:16 by matherp
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 4246
Posted: 04:39pm 18 Sep 2023
Copy link to clipboard 
Print this post

Hi Peter,

On 50708b17, just some observations:

1/
Now you have forced the editor in 80 column mode, is this something to do with "files" also ?

2/
When in mode2, and you type a command that is longer than the 40 characters screen width, the typing continues on the next line (wrap around) but the command typed is not accepted. Only the characters on the new line are seen as the new command.

Thanks for your continued support.

Volhout
PicomiteVGA PETSCII ROBOTS
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 9125
Posted: 04:49pm 18 Sep 2023
Copy link to clipboard 
Print this post

  Quote  When in mode2, and you type a command that is longer than the 40 characters screen width, the typing continues on the next line (wrap around) but the command typed is not accepted. Only the characters on the new line are seen as the new command.


Has always and will always be the case

  Quote  Now you have forced the editor in 80 column mode, is this something to do with "files" also ?


No. Seemed to make sense as it is the mode that allows colour and a reasonable height and width. May look at this again - thoughts?
 
carlschneider
Senior Member

Joined: 04/08/2023
Location: South Africa
Posts: 158
Posted: 05:34pm 18 Sep 2023
Copy link to clipboard 
Print this post

Thanks Peter

  matherp said  It was related to any tokenisable code in the comment. Although the interpreter would skip everything in the comment, the tokeniser would get its knickers in a twist.


Yes that makes sense [in hindsight] and is what I experienced but didn't chase it down, because the sub routine I /*_*/ was required.

All your effort and responsiveness is appreciated.
Cheers Carl                                                        
Retirement is tough on Hobbies without a day job
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 4246
Posted: 06:34pm 18 Sep 2023
Copy link to clipboard 
Print this post

@Peter,

Maybe some change for the user manual of the VGA version.
The manual says sprites are numbered 1-64
The actual software allows only 32 sprites.

Volhout.

EDIT: it is actually 1-31. The error message is shown at the 32'th sprite loaded.
Edited 2023-09-19 16:58 by Volhout
PicomiteVGA PETSCII ROBOTS
 
     Page 11 of 11    
Print this page


To reply to this topic, you need to log in.

© JAQ Software 2024