2025-06-15 21:31:45 +08:00

9192 lines
367 KiB
C

/***********************************************************************************************************************
PicoMite MMBasic
Draw.c
<COPYRIGHT HOLDERS> Geoff Graham, Peter Mather
Copyright (c) 2021, <COPYRIGHT HOLDERS> All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the distribution.
3. The name MMBasic be used when referring to the interpreter in any documentation and promotional material and the original copyright message be displayed
on the console at startup (additional copyright messages may be added).
4. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed
by the <copyright holder>.
5. Neither the name of the <copyright holder> nor the names of its contributors may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDERS> AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDERS> BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
************************************************************************************************************************/
/**
* @file Draw.c
* @author Geoff Graham, Peter Mather
* @brief Source for Graphics MMBasic commands and functions
*/
/*
* @cond
* The following section will be excluded from the documentation.
*/
#include "MMBasic_Includes.h"
#include "Hardware_Includes.h"
#include "hardware/spi.h"
#ifndef PICOMITEWEB
#include "pico/multicore.h"
extern mutex_t frameBufferMutex;
#endif
#ifdef PICOMITEWEB
#include "pico/cyw43_arch.h"
#endif
#define LONG long
#define max(x, y) (((x) > (y)) ? (x) : (y))
#define min(x, y) (((x) < (y)) ? (x) : (y))
void DrawFilledCircle(int x, int y, int radius, int r, int fill, int ints_per_line, uint32_t *br, MMFLOAT aspect, MMFLOAT aspect2);
void hline(int x0, int x1, int y, int f, int ints_per_line, uint32_t *br);
void SaveTriangle(int bnbr, char *buff);
void RestoreTriangle(int bnbr, char *buff);
void ReadLine(int x1,int y1,int x2,int y2, char *buff);
void cmd_RestoreTriangle(unsigned char *p);
void polygon(unsigned char *p, int close);
typedef struct _BMPDECODER
{
LONG lWidth;
LONG lHeight;
LONG lImageOffset;
WORD wPaletteEntries;
BYTE bBitsPerPixel;
BYTE bHeaderType;
BYTE blBmMarkerFlag : 1;
BYTE blCompressionType : 3;
BYTE bNumOfPlanes : 3;
BYTE b16bit565flag : 1;
BYTE aPalette[256][3]; /* Each palette entry has RGB */
} BMPDECODER;
/***************************************************************************/
// define the fonts
#include "font1.h"
#include "Misc_12x20_LE.h"
#include "Hom_16x24_LE.h"
#include "Fnt_10x16.h"
#include "Inconsola.h"
#include "ArialNumFontPlus.h"
#include "Font_8x6.h"
#include "arial_bold.h"
#ifdef PICOMITEVGA
#ifndef HDMI
#include "Include.h"
#endif
#endif
#include "smallfont.h"
unsigned char *FontTable[FONT_TABLE_SIZE] = { (unsigned char *)font1,
(unsigned char *)Misc_12x20_LE,
#ifdef PICOMITEVGA
#ifdef HDMI
(unsigned char *)Hom_16x24_LE,
#else
(unsigned char *)arial_bold,
#endif
#else
(unsigned char *)Hom_16x24_LE,
#endif
(unsigned char *)Fnt_10x16,
(unsigned char *)Inconsola,
(unsigned char *)ArialNumFontPlus,
(unsigned char *)F_6x8_LE,
(unsigned char *)TinyFont,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
/***************************************************************************/
// the default function for DrawRectangle() and DrawBitmap()
short gui_font;
int gui_fcolour;
int gui_bcolour;
short low_y=2000, high_y=-1, low_x=2000, high_x=-1;
int PrintPixelMode=0;
short CurrentX=0, CurrentY=0; // the current default position for the next char to be written
short DisplayHRes, DisplayVRes; // the physical characteristics of the display
struct spritebuffer spritebuff[MAXBLITBUF+1] = { 0 };
struct blitbuffer blitbuff[MAXBLITBUF+1] = { 0 };
char CMM1=0;
// the MMBasic programming characteristics of the display
// note that HRes == 0 is an indication that a display is not configured
short HRes = 0, VRes = 0;
short lastx,lasty;
const int CMM1map[16]={BLACK,BLUE,GREEN,CYAN,RED,MAGENTA,YELLOW,WHITE,MYRTLE,COBALT,MIDGREEN,CERULEAN,RUST,FUCHSIA,BROWN,LILAC};
int RGB121map[16];
uint32_t remap[256];
// pointers to the drawing primitives
#ifndef PICOMITEWEB
struct D3D* struct3d[MAX3D + 1] = { NULL };
s_camera camera[MAXCAM + 1];
#endif
int layer_in_use[MAXLAYER + 1];
unsigned char LIFO[MAXBLITBUF];
unsigned char zeroLIFO[MAXBLITBUF];
uint8_t LIFOpointer = 0;
uint8_t zeroLIFOpointer = 0;
uint8_t sprites_in_use = 0;
char* COLLISIONInterrupt = NULL;
bool CollisionFound = false;
int sprite_which_collided = -1;
static bool hideall = 0;
uint8_t sprite_transparent=0;
#ifdef PICOMITEVGA
short gui_font_width, gui_font_height;
int last_bcolour, last_fcolour;
volatile int CursorTimer=0; // used to time the flashing cursor
extern volatile int QVgaScanLine;
extern uint16_t map16[16];
bool mergedread=0;
int ScreenSize=0;
#else
extern int SSD1963data;
int map[16]={0};
#ifdef PICOMITEWEB
#ifndef rp2350
short gui_font_width, gui_font_height;
int last_bcolour, last_fcolour;
volatile int CursorTimer=0; // used to time the flashing cursor
int display_backlight; // the brightness of the backlight (1 to 100)
#else
#endif
extern int InvokingCtrl;
#else
extern int InvokingCtrl;
bool mergerunning=false;
volatile bool mergedone=false;
uint32_t mergetimer=0;
#endif
#endif
void cmd_ReadTriangle(unsigned char *p);
void (*DrawRectangle)(int x1, int y1, int x2, int y2, int c) = (void (*)(int , int , int , int , int ))DisplayNotSet;
void (*DrawBitmap)(int x1, int y1, int width, int height, int scale, int fc, int bc, unsigned char *bitmap) = (void (*)(int , int , int , int , int , int , int , unsigned char *))DisplayNotSet;
void (*ScrollLCD) (int lines) = (void (*)(int ))DisplayNotSet;
void (*DrawBuffer)(int x1, int y1, int x2, int y2, unsigned char *c) = (void (*)(int , int , int , int , unsigned char * ))DisplayNotSet;
void (*ReadBuffer)(int x1, int y1, int x2, int y2, unsigned char *c) = (void (*)(int , int , int , int , unsigned char * ))DisplayNotSet;
void (*DrawBLITBuffer)(int x1, int y1, int x2, int y2, unsigned char *c) = (void (*)(int , int , int , int , unsigned char * ))DisplayNotSet;
void (*ReadBLITBuffer)(int x1, int y1, int x2, int y2, unsigned char *c) = (void (*)(int , int , int , int , unsigned char * ))DisplayNotSet;
void (*DrawBufferFast)(int x1, int y1, int x2, int y2, int blank, unsigned char *c) = (void (*)(int , int , int , int , int, unsigned char * ))DisplayNotSet;
void (*ReadBufferFast)(int x1, int y1, int x2, int y2, unsigned char *c) = (void (*)(int , int , int , int , unsigned char * ))DisplayNotSet;
void (*DrawPixel)(int x1, int y1, int c) = (void (*)(int , int , int ))DisplayNotSet;
void DrawTriangle(int x0, int y0, int x1, int y1, int x2, int y2, int c, int fill);
// these are the GUI commands that are common to the MX170 and MX470 versions
// in the case of the MX170 this function is called directly by MMBasic when the GUI command is used
// in the case of the MX470 it is called by MX470GUI in GUI.c
const int colours[16]={0x00,0xFF,0x4000,0x40ff,0x8000,0x80ff,0xff00,0xffff,0xff0000,0xff00FF,0xff4000,0xff40ff,0xff8000,0xff80ff,0xffff00,0xffffff};
void MIPS16 initFonts(void){
FontTable[0] = (unsigned char *)font1;
FontTable[1] = (unsigned char *)Misc_12x20_LE;
#ifdef PICOMITEVGA
#ifdef HDMI
FontTable[2] = (unsigned char *)Hom_16x24_LE;
#else
FontTable[2] = (unsigned char *)arial_bold;
#endif
#else
FontTable[2] = (unsigned char *)Hom_16x24_LE;
#endif
FontTable[3] = (unsigned char *)Fnt_10x16;
FontTable[4] = (unsigned char *)Inconsola;
FontTable[5] = (unsigned char *)ArialNumFontPlus;
FontTable[6] = (unsigned char *)F_6x8_LE;
FontTable[7] = (unsigned char *)TinyFont;
FontTable[8] = NULL;
FontTable[9] = NULL;
FontTable[10] = NULL;
FontTable[11] = NULL;
FontTable[12] = NULL;
FontTable[13] = NULL;
FontTable[14] = NULL;
FontTable[15] = NULL;
}
uint16_t __not_in_flash_func(RGB555)(uint32_t c){
return ((c & 0xf8)>>3) | ((c& 0xf800)>>6) | ((c & 0xf80000)>>9);
}
uint8_t __not_in_flash_func(RGB332)(uint32_t c){
return ((c & 0b111000000000000000000000)>>16) | ((c & 0b1110000000000000)>>11) | ((c & 0b11000000)>>6);
}
uint8_t __not_in_flash_func(RGB121)(uint32_t c){
return ((c & 0x800000)>> 20) | ((c & 0xC000)>>13) | ((c & 0x80)>>7);
}
uint16_t __not_in_flash_func(RGB121pack)(uint32_t c){
return (RGB121(c)<<12) | (RGB121(c)<<8) | (RGB121(c)<<4) | RGB121(c);
}
/* @endcond */
void MIPS16 cmd_guiMX170(void) {
unsigned char *p;
if(Option.DISPLAY_TYPE == 0) error("Display not configured");
// display a bitmap stored in an integer or string
if((p = checkstring(cmdline, (unsigned char *)"BITMAP"))) {
int x, y, fc, bc, h, w, scale, t, bytes;
unsigned char *s;
MMFLOAT f;
long long int i64;
getargs(&p, 15, (unsigned char *)",");
if(!(argc & 1) || argc < 5) error("Argument count");
// set the defaults
h = 8; w = 8; scale = 1; bytes = 8; fc = gui_fcolour; bc = gui_bcolour;
x = getinteger(argv[0]);
y = getinteger(argv[2]);
// get the type of argument 3 (the bitmap) and its value (integer or string)
t = T_NOTYPE;
evaluate(argv[4], &f, &i64, &s, &t, true);
if(t & T_NBR)
error("Invalid argument");
else if(t & T_INT)
s = (unsigned char *)&i64;
else if(t & T_STR)
bytes = *s++;
if(argc > 5 && *argv[6]) w = getint(argv[6], 1, HRes);
if(argc > 7 && *argv[8]) h = getint(argv[8], 1, VRes);
if(argc > 9 && *argv[10]) scale = getint(argv[10], 1, 15);
if(argc > 11 && *argv[12]) fc = getint(argv[12], 0, WHITE);
if(argc == 15) bc = getint(argv[14], -1, WHITE);
if(h * w > bytes * 8) error("Not enough data");
DrawBitmap(x, y, w, h, scale, fc, bc, (unsigned char *)s);
if(Option.Refresh)Display_Refresh();
return;
}
#ifndef PICOMITEVGA
#ifdef GUICONTROLS
if((p = checkstring(cmdline, (unsigned char *)"BEEP"))) {
if(Option.TOUCH_Click == 0) error("Click option not set");
ClickTimer = getint(p, 0, INT_MAX) + 1;
return;
}
#endif
if((p = checkstring(cmdline, (unsigned char *)"RESET"))) {
if((checkstring(p, (unsigned char *)"LCDPANEL"))) {
#ifdef PICOMITE
if(mergerunning){
multicore_fifo_push_blocking(0xFF);
busy_wait_ms(mergetimer+200);
if(mergerunning){
_excep_code = RESET_COMMAND;
SoftReset();
}
}
#endif
InitDisplaySPI(true);
InitDisplayI2C(true);
if((Option.TOUCH_CS || Option.TOUCH_IRQ) && !Option.TOUCH_CAP) {
GetTouchValue(CMD_PENIRQ_ON); // send the controller the command to turn on PenIRQ
GetTouchAxis(CMD_MEASURE_X);
}
return;
}
}
if((p = checkstring(cmdline, (unsigned char *)"CALIBRATE"))) {
int tlx, tly, trx, try, blx, bly, brx, bry, midy;
char *s;
if(Option.TOUCH_CS == 0 && Option.TOUCH_IRQ ==0) error("Touch not configured");
if(*p && *p != '\'') { // if the calibration is provided on the command line
getargs(&p, 9, (unsigned char *)",");
if(argc != 9) error("Argument count");
Option.TOUCH_SWAPXY = getinteger(argv[0]);
Option.TOUCH_XZERO = getinteger(argv[2]);
Option.TOUCH_YZERO = getinteger(argv[4]);
Option.TOUCH_XSCALE = getinteger(argv[6]) / 10000.0;
Option.TOUCH_YSCALE = getinteger(argv[8]) / 10000.0;
if(!CurrentLinePtr) SaveOptions();
return;
} else {
if(CurrentLinePtr) error("Invalid in a program");
Option.TOUCH_SWAPXY = 0;
Option.TOUCH_XZERO = 0;
Option.TOUCH_YZERO = 0;
Option.TOUCH_XSCALE = 1.0f;
Option.TOUCH_YSCALE = 1.0f;
}
calibrate=1;
GetCalibration(TARGET_OFFSET, TARGET_OFFSET, &tlx, &tly);
GetCalibration(HRes - TARGET_OFFSET, TARGET_OFFSET, &trx, &try);
if(abs(trx - tlx) < CAL_ERROR_MARGIN && abs(tly - try) < CAL_ERROR_MARGIN) {
calibrate=0;
error("Touch hardware failure %,%,%,%",tlx,trx,tly,try);
}
GetCalibration(TARGET_OFFSET, VRes - TARGET_OFFSET, &blx, &bly);
GetCalibration(HRes - TARGET_OFFSET, VRes - TARGET_OFFSET, &brx, &bry);
calibrate=0;
midy = max(max(tly, try), max(bly, bry)) / 2;
Option.TOUCH_SWAPXY = ((tly < midy && try > midy) || (tly > midy && try < midy));
if(Option.TOUCH_SWAPXY) {
swap(tlx, tly);
swap(trx, try);
swap(blx, bly);
swap(brx, bry);
}
Option.TOUCH_XSCALE = (MMFLOAT)(HRes - TARGET_OFFSET * 2) / (MMFLOAT)(trx - tlx);
Option.TOUCH_YSCALE = (MMFLOAT)(VRes - TARGET_OFFSET * 2) / (MMFLOAT)(bly - tly);
Option.TOUCH_XZERO = ((MMFLOAT)tlx - ((MMFLOAT)TARGET_OFFSET / Option.TOUCH_XSCALE));
Option.TOUCH_YZERO = ((MMFLOAT)tly - ((MMFLOAT)TARGET_OFFSET / Option.TOUCH_YSCALE));
SaveOptions();
brx = (HRes - TARGET_OFFSET) - ((brx - Option.TOUCH_XZERO) * Option.TOUCH_XSCALE);
bry = (VRes - TARGET_OFFSET) - ((bry - Option.TOUCH_YZERO)*Option.TOUCH_YSCALE);
if(abs(brx) > CAL_ERROR_MARGIN || abs(bry) > CAL_ERROR_MARGIN) {
s = "Warning: Inaccurate calibration\r\n";
} else
s = "Done. No errors\r\n";
CurrentX = CurrentY = 0;
MMPrintString(s);
strcpy((char *)inpbuf, "Deviation X = "); IntToStr((char *)inpbuf + strlen((char *)inpbuf), brx, 10);
strcat((char *)inpbuf, ", Y = "); IntToStr((char *)inpbuf + strlen((char *)inpbuf), bry, 10); strcat((char *)inpbuf, " (pixels)\r\n");
MMPrintString((char *)inpbuf);
if(!Option.DISPLAY_CONSOLE) {
GUIPrintString(0, 0, 0x11, JUSTIFY_LEFT, JUSTIFY_TOP, ORIENT_NORMAL, WHITE, BLACK, s);
GUIPrintString(0, 36, 0x11, JUSTIFY_LEFT, JUSTIFY_TOP, ORIENT_NORMAL, WHITE, BLACK, (char *)inpbuf);
}
return;
}
#endif
if((p = checkstring(cmdline, (unsigned char *)"TEST"))) {
if((checkstring(p, (unsigned char *)"LCDPANEL"))) {
int t;
t = ((HRes > VRes) ? HRes : VRes) / 7;
while(getConsole() < '\r') {
routinechecks();
#ifdef PICOMITEWEB
{if(startupcomplete)ProcessWeb(1);}
#endif
DrawCircle(rand() % HRes, rand() % VRes, (rand() % t) + t/5, 1, 1, rgb((rand() % 8)*256/8, (rand() % 8)*256/8, (rand() % 8)*256/8), 1);
#ifdef PICOMITEVGA
#ifdef HDMI
while(v_scanline!=0){}
#else
while(QVgaScanLine!=0){}
#endif
#endif
}
ClearScreen(gui_bcolour);
return;
}
#ifndef PICOMITEVGA
if((checkstring(p, (unsigned char *)"TOUCH"))) {
int x, y;
ClearScreen(gui_bcolour);
while(getConsole() < '\r') {
x = GetTouch(GET_X_AXIS);
y = GetTouch(GET_Y_AXIS);
if(x != TOUCH_ERROR && y != TOUCH_ERROR) DrawBox(x - 1, y - 1, x + 1, y + 1, 0, WHITE, WHITE);
}
ClearScreen(gui_bcolour);
return;
}
#endif
}
error("Unknown command");
}
/*
* @cond
* The following section will be excluded from the documentation.
*/
void getargaddress (unsigned char *p, long long int **ip, MMFLOAT **fp, int *n){
unsigned char *ptr=NULL;
*fp=NULL;
*ip=NULL;
char pp[STRINGSIZE]={0};
strcpy(pp,(char *)p);
if(!isnamestart(pp[0])){ //found a literal
*n=1;
return;
}
ptr = findvar((unsigned char *)pp, V_FIND | V_EMPTY_OK | V_NOFIND_NULL);
if(ptr && g_vartbl[g_VarIndex].type & (T_NBR | T_INT)) {
if(g_vartbl[g_VarIndex].dims[0] <= 0){ //simple variable
*n=1;
return;
} else { // array or array element
if(*n == 0)*n=g_vartbl[g_VarIndex].dims[0] + 1 - g_OptionBase;
else *n = (g_vartbl[g_VarIndex].dims[0] + 1 - g_OptionBase)< *n ? (g_vartbl[g_VarIndex].dims[0] + 1 - g_OptionBase) : *n;
skipspace(p);
do {
p++;
} while(isnamechar(*p));
if(*p == '!' || *p== '%') p++;
if(*p == '(') {
p++;
skipspace(p);
if(*p != ')') { //array element
*n=1;
return;
}
}
}
if(g_vartbl[g_VarIndex].dims[1] != 0) error("Invalid variable");
if(g_vartbl[g_VarIndex].type & T_NBR)*fp = (MMFLOAT*)ptr;
else *ip = (long long int *)ptr;
} else {
*n=1; //may be a function call
}
}
/****************************************************************************************************
General purpose drawing routines
****************************************************************************************************/
int rgb(int r, int g, int b) {
return RGB(r, g, b);
}
void getcoord(char *p, int *x, int *y) {
unsigned char *tp, *ttp;
char b[STRINGSIZE];
char savechar;
tp = getclosebracket((unsigned char *)p);
savechar=*tp;
*tp = 0; // remove the closing brackets
strcpy(b, p); // copy the coordinates to the temp buffer
*tp = savechar; // put back the closing bracket
ttp = (unsigned char *)b+1;
// kludge (todo: fix this)
{
getargs(&ttp, 3, (unsigned char *)","); // this is a macro and must be the first executable stmt in a block
if(argc != 3) error("Invalid Syntax");
*x = getinteger(argv[0]);
*y = getinteger(argv[2]);
}
}
int getColour(char *c, int minus){
int colour;
if(CMM1){
colour = getint((unsigned char *)c,(minus ? -1: 0),15);
if(colour>=0)colour=CMM1map[colour];
} else colour=getint((unsigned char *)c,(minus ? -1: 0),0xFFFFFFF);
return colour;
}
#ifndef PICOMITEVGA
void DrawPixelNormal(int x, int y, int c) {
DrawRectangle(x, y, x, y, c);
}
#endif
void ClearScreen(int c) {
#ifdef PICOMITEVGA
if(DISPLAY_TYPE==SCREENMODE1 && WriteBuf==DisplayBuf){
DrawRectangle(0, 0, HRes - 1, VRes - 1, 0);
#ifdef HDMI
memset((void *)WriteBuf,0,ScreenSize);
if(FullColour){
uint16_t bcolour = RGB555(c);
for(int x=0;x<X_TILE;x++){
for(int y=0;y<Y_TILE;y++){
tilefcols[y*X_TILE+x]=RGB555(gui_fcolour);
tilebcols[y*X_TILE+x]=bcolour;
}
}
} else {
uint8_t bcolour = RGB332(c);
for(int x=0;x<X_TILE;x++){
for(int y=0;y<Y_TILE;y++){
tilefcols_w[y*X_TILE+x]=RGB332(gui_fcolour);
tilebcols_w[y*X_TILE+x]=bcolour;
}
}
}
CurrentX=CurrentY=0;
#else
memset((void *)WriteBuf,0,ScreenSize);
for(int x=0;x<X_TILE;x++){
for(int y=0;y<Y_TILE;y++){
tilefcols[y*X_TILE+x]=RGB121pack(gui_fcolour);
tilebcols[y*X_TILE+x]=RGB121pack(c);
}
}
#endif
} else DrawRectangle(0, 0, HRes - 1, VRes - 1, c);
#else
DrawRectangle(0, 0, HRes - 1, VRes - 1, c);
#endif
}
void DrawBuffered(int xti, int yti, int c, int complete){
static unsigned char pos=0;
static unsigned char movex, movey, movec;
static short xtilast[8];
static short ytilast[8];
static int clast[8];
xtilast[pos]=xti;
ytilast[pos]=yti;
clast[pos]=c;
if(complete==1){
if(pos==1){
DrawPixel(xtilast[0],ytilast[0],clast[0]);
} else {
DrawLine(xtilast[0],ytilast[0],xtilast[pos-1],ytilast[pos-1],1,clast[0]);
}
pos=0;
} else {
if(pos==0){
movex = movey = movec = 1;
pos+=1;
} else {
if(xti==xtilast[0] && abs(yti-ytilast[pos-1])==1)movex=0;else movex=1;
if(yti==ytilast[0] && abs(xti-xtilast[pos-1])==1)movey=0;else movey=1;
if(c==clast[0])movec=0;else movec=1;
if(movec==0 && (movex==0 || movey==0) && pos<6) pos+=1;
else {
if(pos==1){
DrawPixel(xtilast[0],ytilast[0],clast[0]);
} else {
DrawLine(xtilast[0],ytilast[0],xtilast[pos-1],ytilast[pos-1],1,clast[0]);
}
movex = movey = movec = 1;
xtilast[0]=xti;
ytilast[0]=yti;
clast[0]=c;
pos=1;
}
}
}
}
/**************************************************************************************************
Draw a line on a the video output
x1, y1 - the start coordinate
x2, y2 - the end coordinate
w - the width of the line (ignored for diagional lines)
c - the colour to use
***************************************************************************************************/
#define abs( a) (((a)> 0) ? (a) : -(a))
int SizeLine(int x1, int y1, int x2, int y2){
int n=0;
if(y1 == y2) {
return abs(x1-x2)+1;
}
if(x1 == x2) {
return abs(y1-y2)+1;
}
int dx, dy, sx, sy, err, e2;
dx = abs(x2 - x1); sx = x1 < x2 ? 1 : -1;
dy = -abs(y2 - y1); sy = y1 < y2 ? 1 : -1;
err = dx + dy;
while(1) {
n++;
e2 = 2 * err;
if (e2 >= dy) {
if (x1 == x2) break;
err += dy; x1 += sx;
}
if (e2 <= dx) {
if (y1 == y2) break;
err += dx; y1 += sy;
}
}
return n;
}
void ReadLine(int x1,int y1,int x2,int y2, char *buff){
if(y1 == y2 || x1 == x2) {
ReadBuffer(x1, y1, x2, y2, (unsigned char *)buff); // horiz line
return;
}
int dx, dy, sx, sy, err, e2;
dx = abs(x2 - x1); sx = x1 < x2 ? 1 : -1;
dy = -abs(y2 - y1); sy = y1 < y2 ? 1 : -1;
err = dx + dy;
while(1) {
ReadBuffer(x1, y1, x1, y1, (unsigned char *)buff);
buff+=3;
e2 = 2 * err;
if (e2 >= dy) {
if (x1 == x2) break;
err += dy; x1 += sx;
}
if (e2 <= dx) {
if (y1 == y2) break;
err += dx; y1 += sy;
}
}
}
void RestoreLine(int x1,int y1,int x2,int y2, char *buff){
if(y1 == y2 || x1 == x2) {
DrawBuffer(x1, y1, x2, y2, (unsigned char *)buff); // horiz line
return;
}
int dx, dy, sx, sy, err, e2;
dx = abs(x2 - x1); sx = x1 < x2 ? 1 : -1;
dy = -abs(y2 - y1); sy = y1 < y2 ? 1 : -1;
err = dx + dy;
while(1) {
DrawBuffer(x1, y1, x1, y1, (unsigned char *)buff);
buff+=3;
e2 = 2 * err;
if (e2 >= dy) {
if (x1 == x2) break;
err += dy; x1 += sx;
}
if (e2 <= dx) {
if (y1 == y2) break;
err += dx; y1 += sy;
}
}
}
void DrawLine(int x1, int y1, int x2, int y2, int w, int c) {
if(y1 == y2 && w>0) {
DrawRectangle(x1, y1, x2, y2 + w - 1, c); // horiz line
if(Option.Refresh)Display_Refresh();
return;
}
if(x1 == x2 && w>0) {
DrawRectangle(x1, y1, x2 + w - 1, y2, c); // vert line
if(Option.Refresh)Display_Refresh();
return;
}
if(w==1 || w==-1){
int dx, dy, sx, sy, err, e2;
dx = abs(x2 - x1); sx = x1 < x2 ? 1 : -1;
dy = -abs(y2 - y1); sy = y1 < y2 ? 1 : -1;
err = dx + dy;
while(1) {
DrawBuffered(x1, y1, c,0);
e2 = 2 * err;
if (e2 >= dy) {
if (x1 == x2) break;
err += dy; x1 += sx;
}
if (e2 <= dx) {
if (y1 == y2) break;
err += dx; y1 += sy;
}
}
DrawBuffered(0, 0, 0, 1);
} else {
float start,end;
if(w<0){
w=abs(w);
start=-(w / 2.0f);
end=w / 2.0f;
} else {
start=0.0f;
end=w;
}
// Calculate the line direction and length
float dx = x2 - x1;
float dy = y2 - y1;
float length = sqrtf(dx * dx + dy * dy);
// Normalize direction vector
float nx = dx / length;
float ny = dy / length;
// Calculate the perpendicular vector for width
float px = -ny;
float py = nx;
// Half-width adjustment
// Loop through every pixel inside the bounding rectangle of the line
for (int i = 0; i <= length; i++) {
float lineX = x1 + i * nx;
float lineY = y1 + i * ny;
for (float j = start; j <= end; j += 0.25f) { // Finer granularity
float pixelX = lineX + j * px;
float pixelY = lineY + j * py;
DrawPixel(roundf(pixelX), roundf(pixelY), c);
}
}
}
if(Option.Refresh)Display_Refresh();
}
/**********************************************************************************************
Draw a box
x1, y1 - the start coordinate
x2, y2 - the end coordinate
w - the width of the sides of the box (can be zero)
c - the colour to use for sides of the box
fill - the colour to fill the box (-1 for no fill)
***********************************************************************************************/
void DrawBox(int x1, int y1, int x2, int y2, int w, int c, int fill) {
int t;
// make sure the coordinates are in the right sequence
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
if(w > x2 - x1) w = x2 - x1;
if(w > y2 - y1) w = y2 - y1;
if(w > 0) {
w--;
DrawRectangle(x1, y1, x2, y1 + w, c); // Draw the top horiz line
DrawRectangle(x1, y2 - w, x2, y2, c); // Draw the bottom horiz line
DrawRectangle(x1, y1, x1 + w, y2, c); // Draw the left vert line
DrawRectangle(x2 - w, y1, x2, y2, c); // Draw the right vert line
w++;
}
if(fill >= 0)
DrawRectangle(x1 + w, y1 + w, x2 - w, y2 - w, fill);
}
/**********************************************************************************************
Draw a box with rounded corners
x1, y1 - the start coordinate
x2, y2 - the end coordinate
radius - the radius (in pixels) of the arc forming the corners
c - the colour to use for sides
fill - the colour to fill the box (-1 for no fill)
***********************************************************************************************/
void MIPS16 DrawRBox(int x1, int y1, int x2, int y2, int radius, int c, int fill) {
int f, ddF_x, ddF_y, xx, yy;
f = 1 - radius;
ddF_x = 1;
ddF_y = -2 * radius;
xx = 0;
yy = radius;
while(xx < yy) {
if(f >= 0) {
yy-=1;
ddF_y += 2;
f += ddF_y;
}
xx+=1;
ddF_x += 2;
f += ddF_x ;
DrawPixel(x2 + xx - radius, y2 + yy - radius, c); // Bottom Right Corner
DrawPixel(x2 + yy - radius, y2 + xx - radius, c); // ^^^
DrawPixel(x1 - xx + radius, y2 + yy - radius, c); // Bottom Left Corner
DrawPixel(x1 - yy + radius, y2 + xx - radius, c); // ^^^
DrawPixel(x2 + xx - radius, y1 - yy + radius, c); // Top Right Corner
DrawPixel(x2 + yy - radius, y1 - xx + radius, c); // ^^^
DrawPixel(x1 - xx + radius, y1 - yy + radius, c); // Top Left Corner
DrawPixel(x1 - yy + radius, y1 - xx + radius, c); // ^^^
if(fill >= 0) {
DrawLine(x2 + xx - radius - 1, y2 + yy - radius, x1 - xx + radius + 1, y2 + yy - radius, 1, fill);
DrawLine(x2 + yy - radius - 1, y2 + xx - radius, x1 - yy + radius + 1, y2 + xx - radius, 1, fill);
DrawLine(x2 + xx - radius - 1, y1 - yy + radius, x1 - xx + radius + 1, y1 - yy + radius, 1, fill);
DrawLine(x2 + yy - radius - 1, y1 - xx + radius, x1 - yy + radius + 1, y1 - xx + radius, 1, fill);
}
}
if(fill >= 0) DrawRectangle(x1 + 1, y1 + radius, x2 - 1, y2 - radius, fill);
DrawRectangle(x1 + radius - 1, y1, x2 - radius + 1, y1, c); // top side
DrawRectangle(x1 + radius - 1, y2, x2 - radius + 1, y2, c); // botom side
DrawRectangle(x1, y1 + radius, x1, y2 - radius, c); // left side
DrawRectangle(x2, y1 + radius, x2, y2 - radius, c); // right side
if(Option.Refresh)Display_Refresh();
}
/***********************************************************************************************
Draw a circle on the video output
x, y - the center of the circle
radius - the radius of the circle
w - width of the line drawing the circle
c - the colour to use for the circle
fill - the colour to use for the fill or -1 if no fill
aspect - the ration of the x and y axis (a MMFLOAT). 1.0 gives a prefect circle
***********************************************************************************************/
/***********************************************************************************************
Draw a circle on the video output
x, y - the center of the circle
radius - the radius of the circle
w - width of the line drawing the circle
c - the colour to use for the circle
fill - the colour to use for the fill or -1 if no fill
aspect - the ration of the x and y axis (a MMFLOAT). 1.0 gives a prefect circle
***********************************************************************************************/
void DrawCircle(int x, int y, int radius, int w, int c, int fill, MMFLOAT aspect) {
int a, b, P;
int A, B;
int asp;
MMFLOAT aspect2;
if(w>1){
if(fill>=0){ // thick border with filled centre
DrawCircle(x,y,radius,0,c,c,aspect);
aspect2=((aspect*(MMFLOAT)radius)-(MMFLOAT)w)/((MMFLOAT)(radius-w));
DrawCircle(x,y,radius-w,0,fill,fill,aspect2);
} else { //thick border with empty centre
int r1=radius-w,r2=radius, xs=-1,xi=0, i,j,k,m, ll=radius;
if(aspect>1.0)ll=(int)((MMFLOAT)radius*aspect);
int ints_per_line=RoundUptoInt((ll*2)+1)/32;
uint32_t *br=(uint32_t *)GetTempMemory(((ints_per_line+1)*((r2*2)+1))*4);
DrawFilledCircle(x, y, r2, r2, 1, ints_per_line, br, aspect, aspect);
aspect2=((aspect*(MMFLOAT)r2)-(MMFLOAT)w)/((MMFLOAT)r1);
DrawFilledCircle(x, y, r1, r2, 0, ints_per_line, br, aspect, aspect2);
x=(int)((MMFLOAT)x+(MMFLOAT)r2*(1.0-aspect));
for(j=0;j<r2*2+1;j++){
for(i=0;i<ints_per_line;i++){
k=br[i+j*ints_per_line];
for(m=0;m<32;m++){
if(xs==-1 && (k & 0x80000000)){
xs=m;
xi=i;
}
if(xs!=-1 && !(k & 0x80000000)){
DrawRectangle(x-r2+xs+xi*32, y-r2+j, x-r2+m+i*32, y-r2+j, c);
xs=-1;
}
k<<=1;
}
}
if(xs!=-1){
DrawRectangle(x-r2+xs+xi*32, y-r2+j, x-r2+m+i*32, y-r2+j, c);
xs=-1;
}
}
}
} else { //single thickness outline
int w1=w,r1=radius;
if(fill>=0){
while(w >= 0 && radius > 0) {
a = 0;
b = radius;
P = 1 - radius;
asp = aspect * (MMFLOAT)(1 << 10);
do {
A = (a * asp) >> 10;
B = (b * asp) >> 10;
if(fill >= 0 && w >= 0) {
DrawRectangle(x-A, y+b, x+A, y+b, fill);
DrawRectangle(x-A, y-b, x+A, y-b, fill);
DrawRectangle(x-B, y+a, x+B, y+a, fill);
DrawRectangle(x-B, y-a, x+B, y-a, fill);
}
if(P < 0)
P+= 3 + 2*a++;
else
P+= 5 + 2*(a++ - b--);
} while(a <= b);
w--;
radius--;
}
}
if(c!=fill){
w=w1; radius=r1;
while(w >= 0 && radius > 0) {
a = 0;
b = radius;
P = 1 - radius;
asp = aspect * (MMFLOAT)(1 << 10);
do {
A = (a * asp) >> 10;
B = (b * asp) >> 10;
if(w) {
DrawPixel(A+x, b+y, c);
DrawPixel(B+x, a+y, c);
DrawPixel(x-A, b+y, c);
DrawPixel(x-B, a+y, c);
DrawPixel(B+x, y-a, c);
DrawPixel(A+x, y-b, c);
DrawPixel(x-A, y-b, c);
DrawPixel(x-B, y-a, c);
}
if(P < 0)
P+= 3 + 2*a++;
else
P+= 5 + 2*(a++ - b--);
} while(a <= b);
w--;
radius--;
}
}
}
if(Option.Refresh)Display_Refresh();
}
void ClearTriangle(int x0, int y0, int x1, int y1, int x2, int y2, int ints_per_line, uint32_t *br) {
if (x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1) == 0)return;
long a, b, y, last;
long dx01, dy01, dx02, dy02, dx12, dy12, sa, sb;
if (y0 > y1) {
swap(y0, y1);
swap(x0, x1);
}
if (y1 > y2) {
swap(y2, y1);
swap(x2, x1);
}
if (y0 > y1) {
swap(y0, y1);
swap(x0, x1);
}
dx01 = x1 - x0; dy01 = y1 - y0; dx02 = x2 - x0;
dy02 = y2 - y0; dx12 = x2 - x1; dy12 = y2 - y1;
sa = 0; sb = 0;
if(y1 == y2) {
last = y1; //Include y1 scanline
} else {
last = y1 - 1; // Skip it
}
for (y = y0; y <= last; y++){
a = x0 + sa / dy01;
b = x0 + sb / dy02;
sa = sa + dx01;
sb = sb + dx02;
a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
if(a > b)swap(a, b);
hline(a, b, y, 0, ints_per_line, br);
}
sa = dx12 * (y - y1);
sb = dx02 * ( y- y0);
while (y <= y2){
a = x1 + sa / dy12;
b = x0 + sb / dy02;
sa = sa + dx12;
sb = sb + dx02;
a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
if(a > b) swap(a, b);
hline(a, b, y, 0, ints_per_line, br);
y = y + 1;
}
}
#define ABS(X) ((X)>0 ? (X) : (-(X)))
void CalcLine(int x1, int y1, int x2, int y2, short *xmin, short *xmax) {
if(y1 == y2) {
if(y1<0)y1=0;
if(y1>=VRes)y1=VRes-1;
if(y2<0)y2=0;
if(y2>=VRes)y2=VRes-1;
if(x1<xmin[y1])xmin[y1]=x1;
if(x2<xmin[y1])xmin[y1]=x2;
if(x1>xmax[y1])xmax[y1]=x1;
if(x2>xmax[y1])xmax[y1]=x2;
return;
}
if(x1 == x2) {
if(y2<y1)swap(y2,y1);
if(y1<0)y1=0;
if(y1>=VRes)y1=VRes-1;
if(y2<0)y2=0;
if(y2>=VRes)y2=VRes-1;
for(int y=y1;y<=y2;y++) {
if(x1<xmin[y])xmin[y]=x1;
if(x1>xmax[y])xmax[y]=x1;
}
return;
}
// uses a variant of Bresenham's line algorithm:
// https://en.wikipedia.org/wiki/Talk:Bresenham%27s_line_algorithm
if (y1 > y2) {
swap(y1, y2);
swap(x1, x2);
}
if(y1<0)y1=0;
if(y1>=VRes)y1=VRes-1;
if(y2<0)y2=0;
if(y2>=VRes)y2=VRes-1;
int absX = ABS(x1-x2); // absolute value of coordinate distances
int absY = ABS(y1-y2);
int offX = x2<x1 ? 1 : -1; // line-drawing direction offsets
int offY = y2<y1 ? 1 : -1;
int x = x2; // incremental location
int y = y2;
int err;
if(x<xmin[y])xmin[y]=x;
if(x>xmax[y])xmax[y]=x;
if (absX > absY) {
// line is more horizontal; increment along x-axis
err = absX / 2;
while (x != x1) {
err = err - absY;
if (err < 0) {
y += offY;
err += absX;
}
x += offX;
if(x<xmin[y])xmin[y]=x;
if(x>xmax[y])xmax[y]=x;
}
} else {
// line is more vertical; increment along y-axis
err = absY / 2;
while (y != y1) {
err = err - absX;
if (err < 0) {
x += offX;
err += absY;
}
y += offY;
if(x<xmin[y])xmin[y]=x;
if(x>xmax[y])xmax[y]=x;
}
}
}
void DrawTriangle(int x0, int y0, int x1, int y1, int x2, int y2, int c, int f) {
if(x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)==0){ // points are co-linear i.e zero area
if (y0 > y1) {
swap(y0, y1);
swap(x0, x1);
}
if (y1 > y2) {
swap(y2, y1);
swap(x2, x1);
}
if (y0 > y1) {
swap(y0, y1);
swap(x0, x1);
}
DrawLine(x0,y0,x2,y2,1,c);
} else {
if(f == -1){
// draw only the outline
DrawLine(x0, y0, x1, y1, 1, c);
DrawLine(x1, y1, x2, y2, 1, c);
DrawLine(x2, y2, x0, y0, 1, c);
} else {
if (y0 > y1) {
swap(y0, y1);
swap(x0, x1);
}
if (y1 > y2) {
swap(y2, y1);
swap(x2, x1);
}
if (y0 > y1) {
swap(y0, y1);
swap(x0, x1);
}
short *xmin=(short *)GetMemory(VRes*sizeof(short));
short *xmax=(short *)GetMemory(VRes*sizeof(short));
int y;
for(y=y0; y<=y2; y++){
if(y>=0 && y<VRes){
xmin[y]=32767;
xmax[y]=-1;
}
}
CalcLine(x0, y0, x1, y1, xmin, xmax);
CalcLine(x1, y1, x2, y2, xmin, xmax);
CalcLine(x2, y2, x0, y0, xmin, xmax);
for(y=y0;y<=y2;y++){
if(y>=0 && y<VRes)DrawRectangle(xmin[y], y, xmax[y], y, f);
}
// if(c!=f){
DrawLine(x0, y0, x1, y1, 1, c);
DrawLine(x1, y1, x2, y2, 1, c);
DrawLine(x2, y2, x0, y0, 1, c);
// }
FreeMemory((unsigned char *)xmin);
FreeMemory((unsigned char *)xmax);
}
}
}
void RestoreTriangle(int bnbr, char *buff){
short *p=(short *)buff;
int x0=p[0];
int y0=p[1];
int x1=p[2];
int y1=p[3];
int x2=p[4];
int y2=p[5];
char *buffp=(char *)&p[6];
if(x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)==0){ // points are co-linear i.e zero area
if (y0 > y1) {
swap(y0, y1);
swap(x0, x1);
}
if (y1 > y2) {
swap(y2, y1);
swap(x2, x1);
}
if (y0 > y1) {
swap(y0, y1);
swap(x0, x1);
}
RestoreLine(x0,y0,x2,y2,buffp);
} else {
if (y0 > y1) {
swap(y0, y1);
swap(x0, x1);
}
if (y1 > y2) {
swap(y2, y1);
swap(x2, x1);
}
if (y0 > y1) {
swap(y0, y1);
swap(x0, x1);
}
short *xmin=(short *)GetMemory(VRes*sizeof(short));
short *xmax=(short *)GetMemory(VRes*sizeof(short));
int y;
for(y=y0; y<=y2; y++){
if(y>=0 && y<VRes){
xmin[y]=32767;
xmax[y]=-1;
}
}
CalcLine(x0, y0, x1, y1, xmin, xmax);
CalcLine(x1, y1, x2, y2, xmin, xmax);
CalcLine(x2, y2, x0, y0, xmin, xmax);
for(y=y0;y<=y2;y++){
DrawBuffer(xmin[y], y, xmax[y], y, (unsigned char *)buffp);
buffp+=(xmax[y]-xmin[y]+1)*3;
}
FreeMemory((unsigned char *)xmin);
FreeMemory((unsigned char *)xmax);
}
}
void SaveTriangle(int bnbr, char *buff){
short *p=(short *)buff;
int x0=p[0];
int y0=p[1];
int x1=p[2];
int y1=p[3];
int x2=p[4];
int y2=p[5];
char *buffp=(char *)&p[6];
if(x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)==0){ // points are co-linear i.e zero area
if (y0 > y1) {
swap(y0, y1);
swap(x0, x1);
}
if (y1 > y2) {
swap(y2, y1);
swap(x2, x1);
}
if (y0 > y1) {
swap(y0, y1);
swap(x0, x1);
}
ReadLine(x0,y0,x2,y2,buffp);
} else {
if (y0 > y1) {
swap(y0, y1);
swap(x0, x1);
}
if (y1 > y2) {
swap(y2, y1);
swap(x2, x1);
}
if (y0 > y1) {
swap(y0, y1);
swap(x0, x1);
}
short *xmin=(short *)GetMemory(VRes*sizeof(short));
short *xmax=(short *)GetMemory(VRes*sizeof(short));
int y;
for(y=y0; y<=y2; y++){
if(y>=0 && y<VRes){
xmin[y]=32767;
xmax[y]=-1;
}
}
CalcLine(x0, y0, x1, y1, xmin, xmax);
CalcLine(x1, y1, x2, y2, xmin, xmax);
CalcLine(x2, y2, x0, y0, xmin, xmax);
for(y=y0;y<=y2;y++){
ReadBuffer(xmin[y], y, xmax[y], y, (unsigned char *)buffp);
buffp+=(xmax[y]-xmin[y]+1)*3;
}
FreeMemory((unsigned char *)xmin);
FreeMemory((unsigned char *)xmax);
}
}
int SizeTriangle(int x0, int y0, int x1, int y1, int x2, int y2) {
int n=0;
if(x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)==0){ // points are co-linear i.e zero area
if (y0 > y1) {
swap(y0, y1);
swap(x0, x1);
}
if (y1 > y2) {
swap(y2, y1);
swap(x2, x1);
}
if (y0 > y1) {
swap(y0, y1);
swap(x0, x1);
}
return SizeLine(x0,y0,x2,y2);
} else {
if (y0 > y1) {
swap(y0, y1);
swap(x0, x1);
}
if (y1 > y2) {
swap(y2, y1);
swap(x2, x1);
}
if (y0 > y1) {
swap(y0, y1);
swap(x0, x1);
}
short *xmin=(short *)GetMemory(VRes*sizeof(short));
short *xmax=(short *)GetMemory(VRes*sizeof(short));
int y;
for(y=y0; y<=y2; y++){
if(y>=0 && y<VRes){
xmin[y]=32767;
xmax[y]=-1;
}
}
CalcLine(x0, y0, x1, y1, xmin, xmax);
CalcLine(x1, y1, x2, y2, xmin, xmax);
CalcLine(x2, y2, x0, y0, xmin, xmax);
for(y=y0;y<=y2;y++){
n+=(xmax[y]-xmin[y]+1);
}
FreeMemory((unsigned char *)xmin);
FreeMemory((unsigned char *)xmax);
}
return n;
}
/* @endcond */
void cmd_RestoreTriangle(unsigned char *p){
getargs(&p, 1, (unsigned char*)",");
if(*argv[0]=='#')argv[0]++;
int bnbr = getint(argv[0], 1, MAXBLITBUF) - 1; // get the buffer number
if (blitbuff[bnbr].blitbuffptr == NULL) error((char *)"Buffer not in use");
if(blitbuff[bnbr].h!=9999)error("Invalid buffer for restore");
RestoreTriangle(bnbr,blitbuff[bnbr].blitbuffptr);
FreeMemory((unsigned char *)blitbuff[bnbr].blitbuffptr);
blitbuff[bnbr].blitbuffptr = NULL;
}
void cmd_ReadTriangle(unsigned char *p){
int bnbr,x1,x2,x3,y1,y2,y3,size;
getargs(&p, 13, (unsigned char*)",");
if(argc!=13)error((char *)"Syntax");
if(*argv[0]=='#')argv[0]++;
bnbr = getint(argv[0], 1, MAXBLITBUF) - 1; // get the buffer number
if (blitbuff[bnbr].blitbuffptr != NULL) error((char *)"Buffer in use");
x1 = getinteger(argv[2]);
y1 = getinteger(argv[4]);
x2 = getinteger(argv[6]);
y2 = getinteger(argv[8]);
x3 = getinteger(argv[10]);
y3 = getinteger(argv[12]);
size=SizeTriangle(x1,y1,x2,y2,x3,y3);
blitbuff[bnbr].blitbuffptr = GetMemory(size*3+256);
blitbuff[bnbr].h=9999;
short *buff = (short *)blitbuff[bnbr].blitbuffptr;
*buff++=x1;
*buff++=y1;
*buff++=x2;
*buff++=y2;
*buff++=x3;
*buff++=y3;
SaveTriangle(bnbr,blitbuff[bnbr].blitbuffptr);
}
/*
* @cond
* The following section will be excluded from the documentation.
*/
/******************************************************************************************
Print a char on the LCD display
Any characters not in the font will print as a space.
The char is printed at the current location defined by CurrentX and CurrentY
*****************************************************************************************/
void GUIPrintChar(int fnt, int fc, int bc, char c, int orientation) {
unsigned char *p, *fp, *np = NULL, *AllocatedMemory = NULL;
int BitNumber, BitPos, x, y, newx, newy, modx, mody, scale = fnt & 0b1111;
int height, width;
if(PrintPixelMode==1)bc=-1;
if(PrintPixelMode==2){
int s=bc;
bc=fc;
fc=s;
}
if(PrintPixelMode==5){
fc=bc;
bc=-1;
}
// to get the +, - and = chars for font 6 we fudge them by scaling up font 1
if((fnt & 0xf0) == 0x50 && (c == '-' || c == '+' || c == '=')) {
fp = (unsigned char *)FontTable[0];
scale = scale * 4;
} else
fp = (unsigned char *)FontTable[fnt >> 4];
height = fp[1];
width = fp[0];
modx = mody = 0;
if(orientation > ORIENT_VERT){
AllocatedMemory = np = GetMemory(width * height);
if (orientation == ORIENT_INVERTED) {
modx -= width * scale -1;
mody -= height * scale -1;
}
else if (orientation == ORIENT_CCW90DEG) {
mody -= width * scale;
}
else if (orientation == ORIENT_CW90DEG){
modx -= height * scale -1;
}
}
if(c >= fp[2] && c < fp[2] + fp[3]) {
p = fp + 4 + (int)(((c - fp[2]) * height * width) / 8);
if(orientation > ORIENT_VERT) { // non-standard orientation
if (orientation == ORIENT_INVERTED) {
for(y = 0; y < height; y++) {
newy = height - y - 1;
for(x=0; x < width; x++) {
newx = width - x - 1;
if((p[((y * width) + x)/8] >> (((height * width) - ((y * width) + x) - 1) %8)) & 1) {
BitNumber=((newy * width) + newx);
BitPos = 128 >> (BitNumber % 8);
np[BitNumber / 8] |= BitPos;
}
}
}
} else if (orientation == ORIENT_CCW90DEG) {
for(y = 0; y < height; y++) {
newx = y;
for(x = 0; x < width; x++) {
newy = width - x - 1;
if((p[((y * width) + x)/8] >> (((height * width) - ((y * width) + x) - 1) %8)) & 1) {
BitNumber=((newy * height) + newx);
BitPos = 128 >> (BitNumber % 8);
np[BitNumber / 8] |= BitPos;
}
}
}
} else if (orientation == ORIENT_CW90DEG) {
for(y = 0; y < height; y++) {
newx = height - y - 1;
for(x=0; x < width; x++) {
newy = x;
if((p[((y * width) + x)/8] >> (((height * width) - ((y * width) + x) - 1) %8)) & 1) {
BitNumber=((newy * height) + newx);
BitPos = 128 >> (BitNumber % 8);
np[BitNumber / 8] |= BitPos;
}
}
}
}
} else np = p;
if(orientation < ORIENT_CCW90DEG) DrawBitmap(CurrentX + modx, CurrentY + mody, width, height, scale, fc, bc, np);
else DrawBitmap(CurrentX + modx, CurrentY + mody, height, width, scale, fc, bc, np);
} else {
if(orientation < ORIENT_CCW90DEG) DrawRectangle(CurrentX + modx, CurrentY + mody, CurrentX + modx + (width * scale), CurrentY + mody + (height * scale), bc);
else DrawRectangle(CurrentX + modx, CurrentY + mody, CurrentX + modx + (height * scale), CurrentY + mody + (width * scale), bc);
}
// to get the . and degree symbols for font 6 we draw a small circle
if((fnt & 0xf0) == 0x50) {
if(orientation > ORIENT_VERT) {
if(orientation == ORIENT_INVERTED) {
if(c == '.') DrawCircle(CurrentX + modx + (width * scale)/2, CurrentY + mody + 7 * scale, 4 * scale, 0, fc, fc, 1.0);
if(c == 0x60) DrawCircle(CurrentX + modx + (width * scale)/2, CurrentY + mody + (height * scale)- 9 * scale, 6 * scale, 2 * scale, fc, -1, 1.0);
} else if(orientation == ORIENT_CCW90DEG) {
if(c == '.') DrawCircle(CurrentX + modx + (height * scale) - 7 * scale, CurrentY + mody + (width * scale)/2, 4 * scale, 0, fc, fc, 1.0);
if(c == 0x60) DrawCircle(CurrentX + modx + 9 * scale, CurrentY + mody + (width * scale)/2, 6 * scale, 2 * scale, fc, -1, 1.0);
} else if(orientation == ORIENT_CW90DEG) {
if(c == '.') DrawCircle(CurrentX + modx + 7 * scale, CurrentY + mody + (width * scale)/2, 4 * scale, 0, fc, fc, 1.0);
if(c == 0x60) DrawCircle(CurrentX + modx + (height * scale)- 9 * scale, CurrentY + mody + (width * scale)/2, 6 * scale, 2 * scale, fc, -1, 1.0);
}
} else {
if(c == '.') DrawCircle(CurrentX + modx + (width * scale)/2, CurrentY + mody + (height * scale) - 7 * scale, 4 * scale, 0, fc, fc, 1.0);
if(c == 0x60) DrawCircle(CurrentX + modx + (width * scale)/2, CurrentY + mody + 9 * scale, 6 * scale, 2 * scale, fc, -1, 1.0);
}
}
if(orientation == ORIENT_NORMAL) CurrentX += width * scale;
else if (orientation == ORIENT_VERT) CurrentY += height * scale;
else if (orientation == ORIENT_INVERTED) CurrentX -= width * scale;
else if (orientation == ORIENT_CCW90DEG) CurrentY -= width * scale;
else if (orientation == ORIENT_CW90DEG ) CurrentY += width * scale;
if(orientation > ORIENT_VERT) FreeMemory(AllocatedMemory);
}
/******************************************************************************************
Print a string on the LCD display
The string must be a C string (not an MMBasic string)
Any characters not in the font will print as a space.
*****************************************************************************************/
void GUIPrintString(int x, int y, int fnt, int jh, int jv, int jo, int fc, int bc, char *str) {
CurrentX = x; CurrentY = y;
if(jo == ORIENT_NORMAL) {
if(jh == JUSTIFY_CENTER) CurrentX -= (strlen(str) * GetFontWidth(fnt)) / 2;
if(jh == JUSTIFY_RIGHT) CurrentX -= (strlen(str) * GetFontWidth(fnt));
if(jv == JUSTIFY_MIDDLE) CurrentY -= GetFontHeight(fnt) / 2;
if(jv == JUSTIFY_BOTTOM) CurrentY -= GetFontHeight(fnt);
}
else if(jo == ORIENT_VERT) {
if(jh == JUSTIFY_CENTER) CurrentX -= GetFontWidth(fnt)/ 2;
if(jh == JUSTIFY_RIGHT) CurrentX -= GetFontWidth(fnt);
if(jv == JUSTIFY_MIDDLE) CurrentY -= (strlen(str) * GetFontHeight(fnt)) / 2;
if(jv == JUSTIFY_BOTTOM) CurrentY -= (strlen(str) * GetFontHeight(fnt));
}
else if(jo == ORIENT_INVERTED) {
if(jh == JUSTIFY_CENTER) CurrentX += (strlen(str) * GetFontWidth(fnt)) / 2;
if(jh == JUSTIFY_RIGHT) CurrentX += (strlen(str) * GetFontWidth(fnt));
if(jv == JUSTIFY_MIDDLE) CurrentY += GetFontHeight(fnt) / 2;
if(jv == JUSTIFY_BOTTOM) CurrentY += GetFontHeight(fnt);
}
else if(jo == ORIENT_CCW90DEG) {
if(jh == JUSTIFY_CENTER) CurrentX -= GetFontHeight(fnt) / 2;
if(jh == JUSTIFY_RIGHT) CurrentX -= GetFontHeight(fnt);
if(jv == JUSTIFY_MIDDLE) CurrentY += (strlen(str) * GetFontWidth(fnt)) / 2;
if(jv == JUSTIFY_BOTTOM) CurrentY += (strlen(str) * GetFontWidth(fnt));
}
else if(jo == ORIENT_CW90DEG) {
if(jh == JUSTIFY_CENTER) CurrentX += GetFontHeight(fnt) / 2;
if(jh == JUSTIFY_RIGHT) CurrentX += GetFontHeight(fnt);
if(jv == JUSTIFY_MIDDLE) CurrentY -= (strlen(str) * GetFontWidth(fnt)) / 2;
if(jv == JUSTIFY_BOTTOM) CurrentY -= (strlen(str) * GetFontWidth(fnt));
}
while(*str) {
#ifdef GUICONTROLS
if(*str == 0xff && Ctrl[InvokingCtrl].type == 10) {
// fc = rgb(0, 0, 255); // this is specially for GUI FORMATBOX
str++;
GUIPrintChar(fnt, bc, fc, *str++, jo);
} else
#endif
GUIPrintChar(fnt, fc, bc, *str++, jo);
}
}
/****************************************************************************************************
MMBasic commands and functions
****************************************************************************************************/
// get and decode the justify$ string used in TEXT and GUI CAPTION
// the values are returned via pointers
int GetJustification(char *p, int *jh, int *jv, int *jo) {
switch(toupper(*p++)) {
case 'L': *jh = JUSTIFY_LEFT; break;
case 'C': *jh = JUSTIFY_CENTER; break;
case 'R': *jh = JUSTIFY_RIGHT; break;
case 0 : return true;
default: p--;
}
skipspace(p);
switch(toupper(*p++)) {
case 'T': *jv = JUSTIFY_TOP; break;
case 'M': *jv = JUSTIFY_MIDDLE; break;
case 'B': *jv = JUSTIFY_BOTTOM; break;
case 0 : return true;
default: p--;
}
skipspace(p);
switch(toupper(*p++)) {
case 'N': *jo = ORIENT_NORMAL; break; // normal
case 'V': *jo = ORIENT_VERT; break; // vertical text (top to bottom)
case 'I': *jo = ORIENT_INVERTED; break; // inverted
case 'U': *jo = ORIENT_CCW90DEG; break; // rotated CCW 90 degrees
case 'D': *jo = ORIENT_CW90DEG; break; // rotated CW 90 degrees
case 0 : return true;
default: return false;
}
return *p == 0;
}
/* @endcond */
void cmd_text(void) {
int x, y, font, scale, fc, bc;
char *s;
int jh = 0, jv = 0, jo = 0;
getargs(&cmdline, 17, (unsigned char *)","); // this is a macro and must be the first executable stmt
if(Option.DISPLAY_TYPE == 0) error("Display not configured");
if(!(argc & 1) || argc < 5) error("Argument count");
x = getinteger(argv[0]);
y = getinteger(argv[2]);
s = (char *)getCstring(argv[4]);
if(argc > 5 && *argv[6])
if(!GetJustification((char *)argv[6], &jh, &jv, &jo))
if(!GetJustification((char *)getCstring(argv[6]), &jh, &jv, &jo))
error("Justification");;
font = (gui_font >> 4) + 1; scale = (gui_font & 0b1111); fc = gui_fcolour; bc = gui_bcolour; // the defaults
if(argc > 7 && *argv[8]) {
if(*argv[8] == '#') argv[8]++;
font = getint(argv[8], 1, FONT_TABLE_SIZE);
}
if(FontTable[font - 1] == NULL) error("Invalid font #%", font);
if(argc > 9 && *argv[10]) scale = getint(argv[10], 1, 15);
if(argc > 11 && *argv[12]) fc = getint(argv[12], 0, WHITE);
if(argc ==15) bc = getint(argv[14], -1, WHITE);
GUIPrintString(x, y, ((font - 1) << 4) | scale, jh, jv, jo, fc, bc, s);
if(Option.Refresh)Display_Refresh();
}
void cmd_pixel(void) {
if(Option.DISPLAY_TYPE == 0) error("Display not configured");
if(CMM1){
int x, y, value;
getcoord((char *)cmdline, &x, &y);
cmdline = getclosebracket(cmdline) + 1;
while(*cmdline && tokenfunction(*cmdline) != op_equal) cmdline++;
if(!*cmdline) error("Invalid syntax");
++cmdline;
if(!*cmdline) error("Invalid syntax");
value = getColour((char *)cmdline,0);
DrawPixel(x, y, value);
lastx = x; lasty = y;
} else {
int x1, y1, c=0, n=0 ,i, nc=0;
long long int *x1ptr, *y1ptr, *cptr;
MMFLOAT *x1fptr, *y1fptr, *cfptr;
getargs(&cmdline, 5,(unsigned char *)",");
if(!(argc == 3 || argc == 5)) error("Argument count");
getargaddress(argv[0], &x1ptr, &x1fptr, &n);
if(n != 1) getargaddress(argv[2], &y1ptr, &y1fptr, &n);
if(n==1){ //just a single point
c = gui_fcolour; // setup the defaults
x1 = getinteger(argv[0]);
y1 = getinteger(argv[2]);
if(argc == 5)
c = getint(argv[4], -1, WHITE);
else
c = gui_fcolour;
if(c!=-1)DrawPixel(x1, y1, c);
else {
CurrentX=x1;
CurrentY=y1;
}
} else {
c = gui_fcolour; // setup the defaults
if(argc == 5){
getargaddress(argv[4], &cptr, &cfptr, &nc);
if(nc == 1) c = getint(argv[4], 0, WHITE);
else if(nc>1) {
if(nc < n) n=nc; //adjust the dimensionality
for(i=0;i<nc;i++){
c = (cfptr == NULL ? cptr[i] : (int)cfptr[i]);
if(c < 0 || c > WHITE) error("% is invalid (valid is % to %)", (int)c, 0, WHITE);
}
}
}
for(i=0;i<n;i++){
x1 = (x1fptr == NULL ? x1ptr[i] : (int)x1fptr[i]);
y1 = (y1fptr == NULL ? y1ptr[i] : (int)y1fptr[i]);
if(nc > 1) c = (cfptr == NULL ? cptr[i] : (int)cfptr[i]);
DrawPixel(x1, y1, c);
}
}
}
if(Option.Refresh)Display_Refresh();
}
void cmd_circle(void) {
if(Option.DISPLAY_TYPE == 0) error("Display not configured");
if(CMM1){
int x, y, radius, colour, fill;
float aspect;
getargs(&cmdline, 9, (unsigned char *)",");
if(argc%2 == 0 || argc < 3) error("Invalid syntax");
if(*argv[0] != '(') error("Expected opening bracket");
if(toupper(*argv[argc - 1]) == 'F') {
argc -= 2;
fill = true;
} else fill = false;
getcoord((char *)argv[0] , &x, &y);
radius = getinteger(argv[2]);
if(radius == 0) return; //nothing to draw
if(radius < 1) error("Invalid argument");
if(argc > 3 && *argv[4])colour = getColour((char *)argv[4],0);
else colour = gui_fcolour;
if(argc > 5 && *argv[6])
aspect = getnumber(argv[6]);
else
aspect = 1;
DrawCircle(x, y, radius, (fill ? 0:1), colour, (fill ? colour : -1), aspect);
lastx = x; lasty = y;
} else {
int x, y, r, w=0, c=0, f=0, n=0 ,i, nc=0, nw=0, nf=0, na=0;
MMFLOAT a;
long long int *xptr, *yptr, *rptr, *fptr, *wptr, *cptr, *aptr;
MMFLOAT *xfptr, *yfptr, *rfptr, *ffptr, *wfptr, *cfptr, *afptr;
getargs(&cmdline, 13,(unsigned char *)",");
if(!(argc & 1) || argc < 5) error("Argument count");
getargaddress(argv[0], &xptr, &xfptr, &n);
if(n != 1) {
getargaddress(argv[2], &yptr, &yfptr, &n);
getargaddress(argv[4], &rptr, &rfptr, &n);
}
if(n==1){
w = 1; c = gui_fcolour; f = -1; a = 1; // setup the defaults
x = getinteger(argv[0]);
y = getinteger(argv[2]);
r = getinteger(argv[4]);
if(argc > 5 && *argv[6]) w = getint(argv[6], 0, 100);
if(argc > 7 && *argv[8]) a = getnumber(argv[8]);
if(argc > 9 && *argv[10]) c = getint(argv[10], 0, WHITE);
if(argc > 11) f = getint(argv[12], -1, WHITE);
int save_refresh=Option.Refresh;
Option.Refresh=0;
DrawCircle(x, y, r, w, c, f, a);
Option.Refresh=save_refresh;
} else {
w = 1; c = gui_fcolour; f = -1; a = 1; // setup the defaults
if(argc > 5 && *argv[6]) {
getargaddress(argv[6], &wptr, &wfptr, &nw);
if(nw == 1) w = getint(argv[6], 0, 100);
else if(nw>1) {
if(nw > 1 && nw < n) n=nw; //adjust the dimensionality
for(i=0;i<nw;i++){
w = (wfptr == NULL ? wptr[i] : (int)wfptr[i]);
if(w < 0 || w > 100) error("% is invalid (valid is % to %)", (int)w, 0, 100);
}
}
}
if(argc > 7 && *argv[8]){
getargaddress(argv[8], &aptr, &afptr, &na);
if(na == 1) a = getnumber(argv[8]);
if(na > 1 && na < n) n=na; //adjust the dimensionality
}
if(argc > 9 && *argv[10]){
getargaddress(argv[10], &cptr, &cfptr, &nc);
if(nc == 1) c = getint(argv[10], 0, WHITE);
else if(nc>1) {
if(nc > 1 && nc < n) n=nc; //adjust the dimensionality
for(i=0;i<nc;i++){
c = (cfptr == NULL ? cptr[i] : (int)cfptr[i]);
if(c < 0 || c > WHITE) error("% is invalid (valid is % to %)", (int)c, 0, WHITE);
}
}
}
if(argc > 11){
getargaddress(argv[12], &fptr, &ffptr, &nf);
if(nf == 1) f = getint(argv[12], -1, WHITE);
else if(nf>1) {
if(nf > 1 && nf < n) n=nf; //adjust the dimensionality
for(i=0;i<nf;i++){
f = (ffptr == NULL ? fptr[i] : (int)ffptr[i]);
if(f < 0 || f > WHITE) error("% is invalid (valid is % to %)", (int)f, 0, WHITE);
}
}
}
int save_refresh=Option.Refresh;
Option.Refresh=0;
for(i=0;i<n;i++){
x = (xfptr==NULL ? xptr[i] : (int)xfptr[i]);
y = (yfptr==NULL ? yptr[i] : (int)yfptr[i]);
r = (rfptr==NULL ? rptr[i] : (int)rfptr[i])-1;
if(nw > 1) w = (wfptr==NULL ? wptr[i] : (int)wfptr[i]);
if(nc > 1) c = (cfptr==NULL ? cptr[i] : (int)cfptr[i]);
if(nf > 1) f = (ffptr==NULL ? fptr[i] : (int)ffptr[i]);
if(na > 1) a = (afptr==NULL ? (MMFLOAT)aptr[i] : afptr[i]);
DrawCircle(x, y, r, w, c, f, a);
}
Option.Refresh=save_refresh;
}
}
if(Option.Refresh)Display_Refresh();
}
/*
* @cond
* The following section will be excluded from the documentation.
*/
static short xb0,xb1,yb0,yb1;
void GetPixel(int x, int y, int *r, int *g, int *b){
union colourmap
{
char rgbbytes[4];
unsigned int rgb;
} c;
ReadBuffer(x,y,x,y,(unsigned char *)&c.rgb);
*r=c.rgbbytes[2];
*g=c.rgbbytes[1];
*b=c.rgbbytes[0];
}
void MIPS16 drawAAPixel( int x , int y , MMFLOAT alpha, uint32_t c){
int bgR, bgG, bgB;
// Get the current background color of the pixel
GetPixel(x, y, &bgR, &bgG, &bgB);
union colourmap
{
unsigned char rgbbytes[4];
unsigned int rgb;
} col;
col.rgb=c;
col.rgbbytes[0]= (unsigned char)((MMFLOAT)col.rgbbytes[0]*alpha);
col.rgbbytes[0]+=(unsigned char)((MMFLOAT)bgB*(1.0-alpha));
col.rgbbytes[1]= (unsigned char)((MMFLOAT)col.rgbbytes[1]*alpha);
col.rgbbytes[1]+=(unsigned char)((MMFLOAT)bgG*(1.0-alpha));
col.rgbbytes[2]= (unsigned char)((MMFLOAT)col.rgbbytes[2]*alpha);
col.rgbbytes[2]+=(unsigned char)((MMFLOAT)bgR*(1.0-alpha));
if(((x>=xb0 && x<=xb1) && (y>=yb0 && y<=yb1)))DrawPixel(x,y,col.rgb);
}
void MIPS16 drawAALine(MMFLOAT x0 , MMFLOAT y0 , MMFLOAT x1 , MMFLOAT y1, uint32_t c, int w)
{
// Ensure positive integer values for width
if(w < 1) w = 1;
// If drawing a dot, the call drawDot function
//if Math.abs(y1 - y0) < 1.0 && Math.abs(x1 - x0) < 1.0
// #drawDot (x0 + x1) / 2, (y0 + y1) / 2
// return
xb0=x0;xb1=x1;yb0=y0;yb1=y1;
if(xb1<xb0)swap(xb1,xb0);
if(yb1<yb0)swap(yb1,yb0);
// steep means that m > 1
int steep = abs(y1 - y0) >= abs(x1 - x0) ;
// swap the co-ordinates if slope > 1 or we
// draw backwards
if (steep)
{
swap(x0 , y0);
swap(x1 , y1);
}
if (x0 > x1)
{
swap(x0 ,x1);
swap(y0 ,y1);
}
//compute the slope
MMFLOAT dx = x1-x0;
MMFLOAT dy = y1-y0;
MMFLOAT gradient;
if (dx <= 0.0) gradient = 1;
else gradient=dy/dx;
//rotate w
w = w * sqrt(1 + (gradient * gradient));
// Handle first endpoint
MMFLOAT xend = round(x0);
MMFLOAT yend = y0 - (w - 1) * 0.5 + gradient * (xend - x0);
MMFLOAT xgap = 1 - (x0 + 0.5 - xend);
MMFLOAT xpxl1 = xend; //this will be used in the main loop
MMFLOAT ypxl1 = floor(yend);
MMFLOAT fpart = yend - floor(yend);
MMFLOAT rfpart = 1.0 - fpart;
if(steep){
drawAAPixel(ypxl1 , xpxl1, rfpart * xgap, c);
for(int i=1;i<=w;i++) drawAAPixel(ypxl1 + i, xpxl1, 1, c);
drawAAPixel(ypxl1 + w, xpxl1, fpart * xgap, c);
} else {
drawAAPixel(xpxl1, ypxl1 , rfpart * xgap, c);
for(int i=1; i<=w; i++) drawAAPixel(xpxl1, ypxl1 + i, 1, c);
drawAAPixel(xpxl1, ypxl1 + w, fpart * xgap, c);
}
MMFLOAT intery = yend + gradient; // first y-intersection for the main loop
// Handle second endpoint
xend = round(x1);
yend = y1 - (w - 1) * 0.5 + gradient * (xend - x1);
xgap = 1 - (x1 + 0.5 - xend);
MMFLOAT xpxl2 = xend; // this will be used in the main loop
MMFLOAT ypxl2 = floor(yend);
fpart = yend - floor(yend);
rfpart = 1 - fpart;
if(steep){
drawAAPixel(ypxl2 , xpxl2, rfpart * xgap, c);
for(int i=1;i<=w;i++) drawAAPixel(ypxl2 + i, xpxl2, 1, c);
drawAAPixel(ypxl2 + w, xpxl2, fpart * xgap, c);
} else {
drawAAPixel(xpxl2, ypxl2 , rfpart * xgap, c);
for(int i=1; i<=w; i++) drawAAPixel(xpxl2, ypxl2 + i, 1, c);
drawAAPixel(xpxl2, ypxl2 + w, fpart * xgap, c);
}
// main loop
if(steep){
for(int x=xpxl1 + 1; x<=xpxl2; x++){
fpart = intery - floor(intery);
rfpart = 1 - fpart;
MMFLOAT y = floor(intery);
drawAAPixel(y , x, rfpart, c);
for(int i=1;i<w;i++) drawAAPixel(y + i, x, 1, c);
drawAAPixel(y + w, x, fpart, c);
intery = intery + gradient;
}
} else {
for(int x=xpxl1 + 1; x<=xpxl2; x++){
fpart = intery - floor(intery);
rfpart = 1 - fpart;
MMFLOAT y = floor(intery);
drawAAPixel(x, y , rfpart, c);
for(int i=1;i<w;i++) drawAAPixel(x, y + i, 1, c);
drawAAPixel(x, y + w, fpart, c);
intery = intery + gradient;
}
}
}
/* @endcond */
void cmd_line(void) {
if(Option.DISPLAY_TYPE == 0) error("Display not configured");
unsigned char *p;
if(CMM1){
int x1, y1, x2, y2, colour, box, fill;
getargs(&cmdline, 5, (unsigned char *)",");
// check if it is actually a LINE INPUT command
if(argc < 1) error("Invalid syntax");
x1 = lastx; y1 = lasty; colour = gui_fcolour; box = false; fill = false; // set the defaults for optional components
p = argv[0];
if(tokenfunction(*p) != op_subtract) {
// the start point is specified - get the coordinates and step over to where the minus token should be
if(*p != '(') error("Expected opening bracket");
getcoord((char *)p , &x1, &y1);
p = getclosebracket(p) + 1;
skipspace(p);
}
if(tokenfunction(*p) != op_subtract) error("Invalid syntax");
p++;
skipspace(p);
if(*p != '(') error("Expected opening bracket");
getcoord((char *)p , &x2, &y2);
if(argc > 1 && *argv[2]){
colour = getColour((char *)argv[2],0);
}
if(argc == 5) {
box = (strchr((char *)argv[4], 'b') != NULL || strchr((char *)argv[4], 'B') != NULL);
fill = (strchr((char *)argv[4], 'f') != NULL || strchr((char *)argv[4], 'F') != NULL);
}
if(box)
DrawBox(x1, y1, x2, y2, 1, colour, (fill ? colour : -1)); // draw a box
else
DrawLine(x1, y1, x2, y2, 1, colour); // or just a line
lastx = x2; lasty = y2; // save in case the user wants the last value
} else {
int x1, y1, x2, y2, w=0, c=0, n=0 ,i, nc=0, nw=0;
if((p=checkstring(cmdline,(unsigned char *)"PLOT"))){
long long int *y1ptr;
MMFLOAT *y1fptr;
int xs=0,xinc=1;
int ys=0,yinc=1;
getargs(&p, 13,(unsigned char *)",");
getargaddress(argv[0], &y1ptr, &y1fptr, &n);
if(n==1)error("Argument 1 is not an array");
nc=n;
if(argc>=3 && *argv[2])nc=getint(argv[2],1,HRes-1);
if(nc>n)nc=n;
if(argc>=5 && *argv[4])xs=getint(argv[4],0,HRes-1);
if(argc>=7 && *argv[6])xinc=getint(argv[6],1,HRes-1);
if(argc>=9 && *argv[8])ys=getint(argv[8],g_OptionBase,n-2+g_OptionBase);
if(argc>=11 && *argv[10])yinc=getint(argv[10],1,n-1);
c = gui_fcolour; w = 1; // setup the defaults
if(argc == 13) c = getint(argv[12], 0, WHITE);
int y=ys-g_OptionBase;
for(i=0;i<(nc-1);i++){
if(y>=nc)break;
if(y+yinc>=nc)break;
x1 = xs+i*xinc;
y1 = (y1fptr==NULL ? y1ptr[y] : (int)y1fptr[y]);
if(y1<0)y1=0;
if(y1>=VRes)y1=VRes-1;
x2 = xs+(i+1)*xinc;
y2 = (y1fptr==NULL ? y1ptr[y+yinc] : (int)y1fptr[y+yinc]);
if(x1>=HRes)break; //can only get worse so stop now
if(x2>=HRes)x2=HRes-1;
if(y2<0)y2=0;
if(y2>=VRes)y2=VRes-1;
DrawLine(x1, y1, x2, y2, w, c);
y+=yinc;
}
} else if((p=checkstring(cmdline,(unsigned char *)"GRAPH"))){
unsigned char *pp=GetTempMemory(STRINGSIZE);
strcpy((char *)pp,(char *)p);
memmove(&pp[2],pp,strlen((char *)p)+1);
pp[0]='0';
pp[1]=',';
polygon(pp,0);
return;
} else if((p=checkstring(cmdline,(unsigned char *)"AA"))){
MMFLOAT x1, y1, x2, y2;
getargs(&p, 11,(unsigned char *)",");
c = gui_fcolour; ; w = 1; // setup the defaults
x1 = getnumber(argv[0]);
y1 = getnumber(argv[2]);
x2 = getnumber(argv[4]);
y2 = getnumber(argv[6]);
if(argc > 7 && *argv[8]){
w = getint(argv[8], 1, 100);
}
if(argc == 11) c = getint(argv[10], 0, WHITE);
if(x1==x2 || y1==y2)DrawLine(x1, y1, x2, y2, w, c);
else drawAALine(x1, y1, x2, y2, c, w);
return;
} else {
long long int *x1ptr, *y1ptr, *x2ptr, *y2ptr, *wptr, *cptr;
MMFLOAT *x1fptr, *y1fptr, *x2fptr, *y2fptr, *wfptr, *cfptr;
getargs(&cmdline, 11,(unsigned char *)",");
if(!(argc & 1) || argc < 3) error("Argument count");
getargaddress(argv[0], &x1ptr, &x1fptr, &n);
if(n != 1) {
if(argc<7)error("Argument count");
getargaddress(argv[2], &y1ptr, &y1fptr, &n);
getargaddress(argv[4], &x2ptr, &x2fptr, &n);
getargaddress(argv[6], &y2ptr, &y2fptr, &n);
}
if(n==1){
c = gui_fcolour; w = 1; // setup the defaults
x1 = getinteger(argv[0]);
y1 = getinteger(argv[2]);
if(argc>=5 && *argv[4])x2 = getinteger(argv[4]);
else {
x2=CurrentX;CurrentX=x1;
}
if(argc>=7 && *argv[6])y2 = getinteger(argv[6]);
else {
y2=CurrentY;CurrentY=y1;
}
if(x1==CurrentX && y1==CurrentY){
CurrentX=x2;
CurrentY=y2;
}
if(argc > 7 && *argv[8]){
w = getint(argv[8], -100, 100);
if(!w)return;
}
if(argc == 11) c = getint(argv[10], 0, WHITE);
DrawLine(x1, y1, x2, y2, w, c);
} else {
c = gui_fcolour; w = 1; // setup the defaults
if(argc > 7 && *argv[8]){
getargaddress(argv[8], &wptr, &wfptr, &nw);
if(nw == 1) w = getint(argv[8], -100, 100);
else if(nw>1) {
if(nw > 1 && nw < n) n=nw; //adjust the dimensionality
for(i=0;i<nw;i++){
w = (wfptr == NULL ? wptr[i] : (int)wfptr[i]);
if(w < -100 || w > 100) error("% is invalid (valid is % to %)", (int)w, 0, 100);
}
}
}
if(argc == 11){
getargaddress(argv[10], &cptr, &cfptr, &nc);
if(nc == 1) c = getint(argv[10], 0, WHITE);
else if(nc>1) {
if(nc > 1 && nc < n) n=nc; //adjust the dimensionality
for(i=0;i<nc;i++){
c = (cfptr == NULL ? cptr[i] : (int)cfptr[i]);
if(c < 0 || c > WHITE) error("% is invalid (valid is % to %)", (int)c, 0, WHITE);
}
}
}
for(i=0;i<n;i++){
x1 = (x1fptr==NULL ? x1ptr[i] : (int)x1fptr[i]);
y1 = (y1fptr==NULL ? y1ptr[i] : (int)y1fptr[i]);
x2 = (x2fptr==NULL ? x2ptr[i] : (int)x2fptr[i]);
y2 = (y2fptr==NULL ? y2ptr[i] : (int)y2fptr[i]);
if(nw > 1) w = (wfptr==NULL ? wptr[i] : (int)wfptr[i]);
if(nc > 1) c = (cfptr==NULL ? cptr[i] : (int)cfptr[i]);
if(w)DrawLine(x1, y1, x2, y2, w, c);
}
}
}
}
if(Option.Refresh)Display_Refresh();
}
void cmd_box(void) {
int x1, y1, w=0, c=0, f=0, n=0 ,i, nc=0, nw=0, nf=0,hmod,wmod, nwidth=0, nheight=0, width=0, height=0;
long long int *x1ptr, *y1ptr, *wiptr, *hptr, *wptr, *cptr, *fptr;
MMFLOAT *x1fptr, *y1fptr, *wifptr, *hfptr, *wfptr, *cfptr, *ffptr;
getargs(&cmdline, 13,(unsigned char *)",");
if(Option.DISPLAY_TYPE == 0) error("Display not configured");
if(!(argc & 1) || argc < 7) error("Argument count");
getargaddress(argv[0], &x1ptr, &x1fptr, &n);
if(n != 1) {
getargaddress(argv[2], &y1ptr, &y1fptr, &n);
}
if(n == 1){
c = gui_fcolour; w = 1; f = -1; // setup the defaults
x1 = getinteger(argv[0]);
y1 = getinteger(argv[2]);
width = getinteger(argv[4]) ;
height = getinteger(argv[6]) ;
wmod=(width > 0 ? -1 : 1);
hmod=(height > 0 ? -1 : 1);
if(argc > 7 && *argv[8]) w = getint(argv[8], 0, 100);
if(argc > 9 && *argv[10]) c = getint(argv[10], 0, WHITE);
if(argc == 13) f = getint(argv[12], -1, WHITE);
if(width != 0 && height != 0) DrawBox(x1, y1, x1 + width + wmod, y1 + height + hmod, w, c, f);
} else {
getargaddress(argv[4], &wiptr, &wifptr, &nwidth);
if(nwidth==1) width= getint(argv[4], 1, HRes);
else if(nwidth>1) {
if(nwidth > 1 && nwidth < n) n=nwidth; //adjust the dimensionality
for(i=0;i<nwidth;i++){
width = (wifptr == NULL ? wiptr[i] : (int)wifptr[i]);
if(width <1 || width > HRes) error("Width % is invalid (valid is % to %)", (int)width, 1, HRes);
}
}
getargaddress(argv[6], &hptr, &hfptr, &nheight);
if(nheight==1) height= getint(argv[6], 1, VRes);
else if(nheight>1) {
if(nheight > 1 && nheight < n) n=nheight; //adjust the dimensionality
for(i=0;i<nheight;i++){
height = (hfptr == NULL ? hptr[i] : (int)hfptr[i]);
if(height <1 || height > VRes) error("Height % is invalid (valid is % to %)", (int)height, 1, VRes);
}
}
c = gui_fcolour; w = 1; // setup the defaults
if(argc > 7 && *argv[8]){
getargaddress(argv[8], &wptr, &wfptr, &nw);
if(nw == 1) w = getint(argv[8], 0, 100);
else if(nw>1) {
if(nw > 1 && nw < n) n=nw; //adjust the dimensionality
for(i=0;i<nw;i++){
w = (wfptr == NULL ? wptr[i] : (int)wfptr[i]);
if(w < 0 || w > 100) error("% is invalid (valid is % to %)", (int)w, 0, 100);
}
}
}
if(argc > 9 && *argv[10]) {
getargaddress(argv[10], &cptr, &cfptr, &nc);
if(nc == 1) c = getint(argv[10], 0, WHITE);
else if(nc>1) {
if(nc > 1 && nc < n) n=nc; //adjust the dimensionality
for(i=0;i<nc;i++){
c = (cfptr == NULL ? cptr[i] : (int)cfptr[i]);
if(c < 0 || c > WHITE) error("% is invalid (valid is % to %)", (int)c, 0, WHITE);
}
}
}
if(argc == 13){
getargaddress(argv[12], &fptr, &ffptr, &nf);
if(nf == 1) f = getint(argv[12], 0, WHITE);
else if(nf>1) {
if(nf > 1 && nf < n) n=nf; //adjust the dimensionality
for(i=0;i<nf;i++){
f = (ffptr == NULL ? fptr[i] : (int)ffptr[i]);
if(f < -1 || f > WHITE) error("% is invalid (valid is % to %)", (int)f, -1, WHITE);
}
}
}
for(i=0;i<n;i++){
x1 = (x1fptr==NULL ? x1ptr[i] : (int)x1fptr[i]);
y1 = (y1fptr==NULL ? y1ptr[i] : (int)y1fptr[i]);
if(nwidth > 1) width = (wifptr==NULL ? wiptr[i] : (int)wifptr[i]);
if(nheight > 1) height = (hfptr==NULL ? hptr[i] : (int)hfptr[i]);
wmod=(width > 0 ? -1 : 1);
hmod=(height > 0 ? -1 : 1);
if(nw > 1) w = (wfptr==NULL ? wptr[i] : (int)wfptr[i]);
if(nc > 1) c = (cfptr==NULL ? cptr[i] : (int)cfptr[i]);
if(nf > 1) f = (ffptr==NULL ? fptr[i] : (int)ffptr[i]);
if(width != 0 && height != 0) DrawBox(x1, y1, x1 + width + wmod, y1 + height + hmod, w, c, f);
}
}
if(Option.Refresh)Display_Refresh();
}
/*
* @cond
* The following section will be excluded from the documentation.
*/
void MIPS16 bezier(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, int c){
float tmp,tmp1,tmp2,tmp3,tmp4,tmp5,tmp6,tmp7,tmp8,t=0.0,xt=x0,yt=y0;
int i, xti,yti,xtlast=-1, ytlast=-1;
for (i=0; i<500; i++)
{
tmp = 1.0 - t;
tmp3 = t * t;
tmp4 = tmp * tmp;
tmp1 = tmp3 * t;
tmp2 = tmp4 * tmp;
tmp5 = 3.0 * t;
tmp6 = 3.0 * tmp3;
tmp7 = tmp5 * tmp4;
tmp8 = tmp6 * tmp;
xti=(int)xt;
yti=(int)yt;
xt = ((((tmp2 * x0) + (tmp7 * x1)) + (tmp8 * x2)) + (tmp1 * x3));
yt = ((((tmp2 * y0) + (tmp7 * y1)) + (tmp8 * y2)) +(tmp1 * y3));
if((xti!=xtlast) || (yti!=ytlast)) {
DrawBuffered(xti, yti, c, 0);
xtlast=xti;
ytlast=yti;
}
t+=0.002;
}
DrawBuffered(0, 0, 0, 1);
}
void MIPS16 pointcalc(int angle, int x, int y, int r2, int *x0, int * y0){
float c1,s1;
int quad;
angle %=360;
switch(angle){
case 0:
*x0=x;
*y0=y-r2;
break;
case 45:
*x0=x+r2+1;
*y0=y-r2;
break;
case 90:
*x0=x+r2+1;
*y0=y;
break;
case 135:
*x0=x+r2+1;
*y0=y+r2;
break;
case 180:
*x0=x;
*y0=y+r2;
break;
case 225:
*x0=x-r2;
*y0=y+r2;
break;
case 270:
*x0=x-r2;
*y0=y;
break;
case 315:
*x0=x-r2;
*y0=y-r2;
break;
default:
c1=cos(Rad(angle));
s1=sin(Rad(angle));
quad = (angle / 45) % 8;
switch(quad){
case 0:
*y0=y-r2;
*x0=x+s1*r2/c1;
break;
case 1:
*x0=x+r2+1;
*y0=y-c1*r2/s1;
break;
case 2:
*x0=x+r2+1;
*y0=y-c1*r2/s1;
break;
case 3:
*y0=y+r2;
*x0=x-s1*r2/c1;
break;
case 4:
*y0=y+r2;
*x0=x-s1*r2/c1;
break;
case 5:
*x0=x-r2;
*y0=y+c1*r2/s1;
break;
case 6:
*x0=x-r2;
*y0=y+c1*r2/s1;
break;
case 7:
*y0=y-r2;
*x0=x+s1*r2/c1;
break;
}
}
}
/* @endcond */
void MIPS16 cmd_arc(void){
// Parameters are:
// X coordinate of centre of arc
// Y coordinate of centre of arc
// inner radius of arc
// outer radius of arc - omit it 1 pixel wide
// start radial of arc in degrees
// end radial of arc in degrees
// Colour of arc
int x, y, r1, r2, c ,i ,j, k, xs=-1, xi=0, m;
int rad1, rad2, rad3, rstart, quadr;
int x0, y0, x1, y1, x2, y2, xr, yr;
getargs(&cmdline, 13,(unsigned char *)",");
if(!(argc == 11 || argc == 13)) error("Argument count");
if(Option.DISPLAY_TYPE == 0) error("Display not configured");
x = getinteger(argv[0]);
y = getinteger(argv[2]);
r1 = getinteger(argv[4]);
if(*argv[6])r2 = getinteger(argv[6]);
else {
r2=r1;
r1--;
}
if(r2 < r1)error("Inner radius < outer");
rad1 = getnumber(argv[8]);
rad2 = getnumber(argv[10]);
while(rad1<0.0)rad1+=360.0;
while(rad2<0.0)rad2+=360.0;
if(rad1==rad2)error("Radials");
if(argc == 13)
c = getint(argv[12], 0, WHITE);
else
c = gui_fcolour;
while(rad2<rad1)rad2+=360;
rad3=rad1+360;
rstart=rad2;
int quad1 = (rad1 / 45) % 8;
x2=x;y2=y;
int ints_per_line=RoundUptoInt((r2*2)+1)/32;
uint32_t *br=(uint32_t *)GetTempMemory(((ints_per_line+1)*((r2*2)+1))*4);
DrawFilledCircle(x, y, r2, r2, 1, ints_per_line, br, 1.0, 1.0);
DrawFilledCircle(x, y, r1, r2, 0, ints_per_line, br, 1.0, 1.0);
while(rstart<rad3){
pointcalc(rstart, x, y, r2, &x0, &y0);
quadr = (rstart / 45) % 8;
if(quadr==quad1 && rad3-rstart<45){
pointcalc(rad3, x, y, r2, &x1, &y1);
ClearTriangle(x0-x+r2, y0-y+r2, x1-x+r2, y1-y+r2, x2-x+r2, y2-y+r2, ints_per_line, br);
rstart=rad3;
} else {
rstart +=45;
rstart -= (rstart % 45);
pointcalc(rstart, x, y, r2, &xr, &yr);
ClearTriangle(x0-x+r2, y0-y+r2, xr-x+r2, yr-y+r2, x2-x+r2, y2-y+r2, ints_per_line, br);
}
}
int save_refresh=Option.Refresh;
Option.Refresh=0;
for(j=0;j<r2*2+1;j++){
for(i=0;i<ints_per_line;i++){
k=br[i+j*ints_per_line];
for(m=0;m<32;m++){
if(xs==-1 && (k & 0x80000000)){
xs=m;
xi=i;
}
if(xs!=-1 && !(k & 0x80000000)){
DrawRectangle(x-r2+xs+xi*32, y-r2+j, x-r2+m+i*32, y-r2+j, c);
xs=-1;
}
k<<=1;
}
}
if(xs!=-1){
DrawRectangle(x-r2+xs+xi*32, y-r2+j, x-r2+m+i*32, y-r2+j, c);
xs=-1;
}
}
Option.Refresh=save_refresh;
if(Option.Refresh)Display_Refresh();
}
/*
* @cond
* The following section will be excluded from the documentation.
*/
typedef struct {
unsigned char red;
unsigned char green;
unsigned char blue;
} rgb_t;
#define TFLOAT float
typedef struct {
TFLOAT xpos; // current position and heading
TFLOAT ypos; // (uses floating-point numbers for
TFLOAT heading; // increased accuracy)
rgb_t pen_color; // current pen color
rgb_t fill_color; // current fill color
bool pendown; // currently drawing?
bool filled; // currently filling?
} fill_t;
fill_t main_fill;
fill_t backup_fill;
int main_fill_poly_vertex_count = 0; // polygon vertex count
TFLOAT *main_fill_polyX=NULL; // polygon vertex x-coords
TFLOAT *main_fill_polyY=NULL; // polygon vertex y-coords
void fill_set_pen_color(int red, int green, int blue)
{
main_fill.pen_color.red = red;
main_fill.pen_color.green = green;
main_fill.pen_color.blue = blue;
}
void fill_set_fill_color(int red, int green, int blue)
{
main_fill.fill_color.red = red;
main_fill.fill_color.green = green;
main_fill.fill_color.blue = blue;
}
static void fill_begin_fill()
{
main_fill.filled = true;
main_fill_poly_vertex_count = 0;
}
static void fill_end_fill(int count, int ystart, int yend)
{
// based on public-domain fill algorithm in C by Darel Rex Finley, 2007
// from http://alienryderflex.com/polygon_fill/
TFLOAT *nodeX=GetMemory(count * sizeof(TFLOAT)); // x-coords of polygon intercepts
int nodes; // size of nodeX
int y, i, j; // current pixel and loop indices
TFLOAT temp; // temporary variable for sorting
int f=(main_fill.fill_color.red<<16) | (main_fill.fill_color.green<<8) | main_fill.fill_color.blue;
int c= (main_fill.pen_color.red<<16) | (main_fill.pen_color.green<<8) | main_fill.pen_color.blue;
int xstart, xend;
// loop through the rows of the image
for (y = ystart; y < yend; y++) {
// build a list of polygon intercepts on the current line
nodes = 0;
j = main_fill_poly_vertex_count-1;
for (i = 0; i < main_fill_poly_vertex_count; i++) {
if ((main_fill_polyY[i] < (TFLOAT)y &&
main_fill_polyY[j] >= (TFLOAT)y) ||
(main_fill_polyY[j] < (TFLOAT)y &&
main_fill_polyY[i] >= (TFLOAT)y)) {
// intercept found; record it
nodeX[nodes++] = (main_fill_polyX[i] +
((TFLOAT)y - main_fill_polyY[i]) /
(main_fill_polyY[j] - main_fill_polyY[i]) *
(main_fill_polyX[j] - main_fill_polyX[i]));
}
j = i;
}
// sort the nodes via simple insertion sort
for (i = 1; i < nodes; i++) {
temp = nodeX[i];
for (j = i; j > 0 && temp < nodeX[j-1]; j--) {
nodeX[j] = nodeX[j-1];
}
nodeX[j] = temp;
}
// fill the pixels between node pairs
for (i = 0; i < nodes; i += 2) {
xstart=(int)floorf(nodeX[i])+1;
xend=(int)ceilf(nodeX[i+1])-1;
DrawLine(xstart,y,xend,y,1, f);
}
}
main_fill.filled = false;
// redraw polygon (filling is imperfect and can occasionally occlude sides)
for (i = 0; i < main_fill_poly_vertex_count; i++) {
int x0 = (int)roundf(main_fill_polyX[i]);
int y0 = (int)roundf(main_fill_polyY[i]);
int x1 = (int)roundf(main_fill_polyX[(i+1) %
main_fill_poly_vertex_count]);
int y1 = (int)roundf(main_fill_polyY[(i+1) %
main_fill_poly_vertex_count]);
DrawLine(x0, y0, x1, y1, 1, c);
}
FreeMemory((void *)nodeX);
}
void polygon(unsigned char *p, int close){
int xcount=0;
long long int *xptr=NULL, *yptr=NULL,xptr2=0, yptr2=0, *polycount=NULL, *cptr=NULL, *fptr=NULL;
MMFLOAT *polycountf=NULL, *cfptr=NULL, *ffptr=NULL, *xfptr=NULL, *yfptr=NULL, xfptr2=0, yfptr2=0;
int i, f=0, c, xtot=0, ymax=0, ymin=1000000;
int n=0, nx=0, ny=0, nc=0, nf=0;
getargs(&p, 9,(unsigned char *)",");
if(Option.DISPLAY_TYPE == 0) error("Display not configured");
getargaddress(argv[0], &polycount, &polycountf, &n);
if(n==1){
xcount = xtot = getinteger(argv[0]);
if((xcount<3 || xcount>9999) && xcount!=0)error("Invalid number of vertices");
getargaddress(argv[2], &xptr, &xfptr, &nx);
if(xcount==0){
xcount = xtot = nx;
}
if(nx<xtot)error("X Dimensions %", nx);
getargaddress(argv[4], &yptr, &yfptr, &ny);
if(ny<xtot)error("Y Dimensions %", ny);
if(xptr)xptr2=*xptr;
else xfptr2=*xfptr;
if(yptr)yptr2=*yptr;
else yfptr2=*yfptr;
c = gui_fcolour; // setup the defaults
if(argc > 5 && *argv[6]) c = getint(argv[6], 0, WHITE);
if(argc > 7 && *argv[8]){
main_fill_polyX=(TFLOAT *)GetTempMemory((xtot+1) * sizeof(TFLOAT));
main_fill_polyY=(TFLOAT *)GetTempMemory((xtot+1) * sizeof(TFLOAT));
f = getint(argv[8], 0, WHITE);
fill_set_fill_color((f>>16) & 0xFF, (f>>8) & 0xFF , f & 0xFF);
fill_set_pen_color((c>>16) & 0xFF, (c>>8) & 0xFF , c & 0xFF);
fill_begin_fill();
}
for(i=0;i<xcount-1;i++){
if(argc > 7){
main_fill_polyX[main_fill_poly_vertex_count] = (xfptr==NULL ? (TFLOAT )*xptr++ : (TFLOAT )*xfptr++) ;
main_fill_polyY[main_fill_poly_vertex_count] = (yfptr==NULL ? (TFLOAT )*yptr++ : (TFLOAT )*yfptr++) ;
if(main_fill_polyY[main_fill_poly_vertex_count]>ymax)ymax=main_fill_polyY[main_fill_poly_vertex_count];
if(main_fill_polyY[main_fill_poly_vertex_count]<ymin)ymin=main_fill_polyY[main_fill_poly_vertex_count];
main_fill_poly_vertex_count++;
} else {
int x1=(xfptr==NULL ? *xptr++ : (int)*xfptr++);
int x2=(xfptr==NULL ? *xptr : (int)*xfptr);
int y1=(yfptr==NULL ? *yptr++ : (int)*yfptr++);
int y2=(yfptr==NULL ? *yptr : (int)*yfptr);
DrawLine(x1,y1,x2,y2, 1, c);
}
}
if(argc > 7){
main_fill_polyX[main_fill_poly_vertex_count] = (xfptr==NULL ? (TFLOAT )*xptr++ : (TFLOAT )*xfptr++) ;
main_fill_polyY[main_fill_poly_vertex_count] = (yfptr==NULL ? (TFLOAT )*yptr++ : (TFLOAT )*yfptr++) ;
if(main_fill_polyY[main_fill_poly_vertex_count]>ymax)ymax=main_fill_polyY[main_fill_poly_vertex_count];
if(main_fill_polyY[main_fill_poly_vertex_count]<ymin)ymin=main_fill_polyY[main_fill_poly_vertex_count];
if(main_fill_polyY[main_fill_poly_vertex_count]!=main_fill_polyY[0] || main_fill_polyX[main_fill_poly_vertex_count] != main_fill_polyX[0]){
main_fill_poly_vertex_count++;
main_fill_polyX[main_fill_poly_vertex_count]=main_fill_polyX[0];
main_fill_polyY[main_fill_poly_vertex_count]=main_fill_polyY[0];
}
main_fill_poly_vertex_count++;
if(main_fill_poly_vertex_count>5){
fill_end_fill(xcount,ymin,ymax);
} else if(main_fill_poly_vertex_count==5){
DrawTriangle(main_fill_polyX[0],main_fill_polyY[0],main_fill_polyX[1],main_fill_polyY[1],main_fill_polyX[2],main_fill_polyY[2],f,f);
DrawTriangle(main_fill_polyX[0],main_fill_polyY[0],main_fill_polyX[2],main_fill_polyY[2],main_fill_polyX[3],main_fill_polyY[3],f,f);
if(f!=c){
DrawLine(main_fill_polyX[0],main_fill_polyY[0],main_fill_polyX[1],main_fill_polyY[1],1,c);
DrawLine(main_fill_polyX[1],main_fill_polyY[1],main_fill_polyX[2],main_fill_polyY[2],1,c);
DrawLine(main_fill_polyX[2],main_fill_polyY[2],main_fill_polyX[3],main_fill_polyY[3],1,c);
DrawLine(main_fill_polyX[3],main_fill_polyY[3],main_fill_polyX[4],main_fill_polyY[4],1,c);
}
} else {
DrawTriangle(main_fill_polyX[0],main_fill_polyY[0],main_fill_polyX[1],main_fill_polyY[1],main_fill_polyX[2],main_fill_polyY[2],c,f);
}
} else if(close){
int x1=(xfptr==NULL ? *xptr : (int)*xfptr);
int x2=(xfptr==NULL ? xptr2 : (int)xfptr2);
int y1=(yfptr==NULL ? *yptr : (int)*yfptr);
int y2=(yfptr==NULL ? yptr2 : (int)yfptr2);
DrawLine(x1,y1,x2,y2, 1, c);
}
} else {
int *cc=GetTempMemory(n*sizeof(int)); //array for foreground colours
int *ff=GetTempMemory(n*sizeof(int)); //array for background colours
int xstart ,j, xmax=0;
for(i=0;i<n;i++){
if((polycountf == NULL ? polycount[i] : (int)polycountf[i])>xmax)xmax=(polycountf == NULL ? polycount[i] : (int)polycountf[i]);
if(!(polycountf == NULL ? polycount[i] : (int)polycountf[i]))break;
xtot+=(polycountf == NULL ? polycount[i] : (int)polycountf[i]);
if((polycountf == NULL ? polycount[i] : (int)polycountf[i])<3 || (polycountf == NULL ? polycount[i] : (int)polycountf[i])>9999)error("Invalid number of vertices, polygon %",i);
}
n=i;
getargaddress(argv[2], &xptr, &xfptr, &nx);
if(nx<xtot)error("X Dimensions %", nx);
getargaddress(argv[4], &yptr, &yfptr, &ny);
if(ny<xtot)error("Y Dimensions %", ny);
main_fill_polyX=(TFLOAT *)GetTempMemory(xmax * sizeof(TFLOAT));
main_fill_polyY=(TFLOAT *)GetTempMemory(xmax * sizeof(TFLOAT));
if(argc > 5 && *argv[6]){ //foreground colour specified
getargaddress(argv[6], &cptr, &cfptr, &nc);
if(nc == 1) for(i=0;i<n;i++)cc[i] = getint(argv[6], 0, WHITE);
else {
if(nc < n) error("Foreground colour Dimensions");
for(i=0;i<n;i++){
cc[i] = (cfptr == NULL ? cptr[i] : (int)cfptr[i]);
if(cc[i] < 0 || cc[i] > 0xFFFFFF) error("% is invalid (valid is % to %)", (int)cc[i], 0, 0xFFFFFF);
}
}
} else for(i=0;i<n;i++)cc[i] = gui_fcolour;
if(argc > 7){ //background colour specified
getargaddress(argv[8], &fptr, &ffptr, &nf);
if(nf == 1) for(i=0;i<n;i++) ff[i] = getint(argv[8], 0, WHITE);
else {
if(nf < n) error("Background colour Dimensions");
for(i=0;i<n;i++){
ff[i] = (ffptr == NULL ? fptr[i] : (int)ffptr[i]);
if(ff[i] < 0 || ff[i] > 0xFFFFFF) error("% is invalid (valid is % to %)", (int)ff[i], 0, 0xFFFFFF);
}
}
}
xstart=0;
for(i=0;i<n;i++){
if(xptr)xptr2=*xptr;
else xfptr2=*xfptr;
if(yptr)yptr2=*yptr;
else yfptr2=*yfptr;
ymax=0;
ymin=1000000;
main_fill_poly_vertex_count=0;
xcount = (int)(polycountf == NULL ? polycount[i] : (int)polycountf[i]);
if(argc > 7 && *argv[8]){
fill_set_pen_color((cc[i]>>16) & 0xFF, (cc[i]>>8) & 0xFF , cc[i] & 0xFF);
fill_set_fill_color((ff[i]>>16) & 0xFF, (ff[i]>>8) & 0xFF , ff[i] & 0xFF);
fill_begin_fill();
}
for(j=xstart;j<xstart+xcount-1;j++){
if(argc > 7){
main_fill_polyX[main_fill_poly_vertex_count] = (xfptr==NULL ? (TFLOAT )*xptr++ : (TFLOAT )*xfptr++) ;
main_fill_polyY[main_fill_poly_vertex_count] = (yfptr==NULL ? (TFLOAT )*yptr++ : (TFLOAT )*yfptr++) ;
if(main_fill_polyY[main_fill_poly_vertex_count]>ymax)ymax=main_fill_polyY[main_fill_poly_vertex_count];
if(main_fill_polyY[main_fill_poly_vertex_count]<ymin)ymin=main_fill_polyY[main_fill_poly_vertex_count];
main_fill_poly_vertex_count++;
} else {
int x1=(xfptr==NULL ? *xptr++ : (int)*xfptr++);
int x2=(xfptr==NULL ? *xptr : (int)*xfptr);
int y1=(yfptr==NULL ? *yptr++ : (int)*yfptr++);
int y2=(yfptr==NULL ? *yptr : (int)*yfptr);
DrawLine(x1,y1,x2,y2, 1, cc[i]);
}
}
if(argc > 7){
main_fill_polyX[main_fill_poly_vertex_count] = (xfptr==NULL ? (TFLOAT )*xptr++ : (TFLOAT )*xfptr++) ;
main_fill_polyY[main_fill_poly_vertex_count] = (yfptr==NULL ? (TFLOAT )*yptr++ : (TFLOAT )*yfptr++) ;
if(main_fill_polyY[main_fill_poly_vertex_count]>ymax)ymax=main_fill_polyY[main_fill_poly_vertex_count];
if(main_fill_polyY[main_fill_poly_vertex_count]<ymin)ymin=main_fill_polyY[main_fill_poly_vertex_count];
if(main_fill_polyY[main_fill_poly_vertex_count]!=main_fill_polyY[0] || main_fill_polyX[main_fill_poly_vertex_count] != main_fill_polyX[0]){
main_fill_poly_vertex_count++;
main_fill_polyX[main_fill_poly_vertex_count]=main_fill_polyX[0];
main_fill_polyY[main_fill_poly_vertex_count]=main_fill_polyY[0];
}
main_fill_poly_vertex_count++;
if(main_fill_poly_vertex_count>5){
fill_end_fill(xcount,ymin,ymax);
} else if(main_fill_poly_vertex_count==5){
DrawTriangle(main_fill_polyX[0],main_fill_polyY[0],main_fill_polyX[1],main_fill_polyY[1],main_fill_polyX[2],main_fill_polyY[2],ff[i],ff[i]);
DrawTriangle(main_fill_polyX[0],main_fill_polyY[0],main_fill_polyX[2],main_fill_polyY[2],main_fill_polyX[3],main_fill_polyY[3],ff[i],ff[i]);
if(ff[i]!=cc[i]){
DrawLine(main_fill_polyX[0],main_fill_polyY[0],main_fill_polyX[1],main_fill_polyY[1],1,cc[i]);
DrawLine(main_fill_polyX[1],main_fill_polyY[1],main_fill_polyX[2],main_fill_polyY[2],1,cc[i]);
DrawLine(main_fill_polyX[2],main_fill_polyY[2],main_fill_polyX[3],main_fill_polyY[3],1,cc[i]);
DrawLine(main_fill_polyX[3],main_fill_polyY[3],main_fill_polyX[4],main_fill_polyY[4],1,cc[i]);
}
} else {
DrawTriangle(main_fill_polyX[0],main_fill_polyY[0],main_fill_polyX[1],main_fill_polyY[1],main_fill_polyX[2],main_fill_polyY[2],cc[i],ff[i]);
}
} else {
int x1=(xfptr==NULL ? *xptr : (int)*xfptr);
int x2=(xfptr==NULL ? xptr2 : (int)xfptr2);
int y1=(yfptr==NULL ? *yptr : (int)*yfptr);
int y2=(yfptr==NULL ? yptr2 : (int)yfptr2);
DrawLine(x1,y1,x2,y2, 1, cc[i]);
if(xfptr!=NULL)xfptr++;
else xptr++;
if(yfptr!=NULL)yfptr++;
else yptr++;
}
xstart+=xcount;
}
}
}
/* @endcond */
void cmd_polygon(void){
polygon(cmdline,1);
}
void MIPS16 cmd_rbox(void) {
int x1, y1, wi, h, w=0, c=0, f=0, r=0, n=0 ,i, nc=0, nw=0, nf=0,hmod,wmod;
long long int *x1ptr, *y1ptr, *wiptr, *hptr, *wptr, *cptr, *fptr;
MMFLOAT *x1fptr, *y1fptr, *wifptr, *hfptr, *wfptr, *cfptr, *ffptr;
getargs(&cmdline, 13,(unsigned char*)",");
if(Option.DISPLAY_TYPE == 0) error("Display not configured");
if(!(argc & 1) || argc < 7) error("Argument count");
getargaddress(argv[0], &x1ptr, &x1fptr, &n);
if(n != 1) {
getargaddress(argv[2], &y1ptr, &y1fptr, &n);
getargaddress(argv[4], &wiptr, &wifptr, &n);
getargaddress(argv[6], &hptr, &hfptr, &n);
}
if(n == 1){
c = gui_fcolour; w = 1; f = -1; r = 10; // setup the defaults
x1 = getinteger(argv[0]);
y1 = getinteger(argv[2]);
w = getinteger(argv[4]);
h = getinteger(argv[6]);
wmod=(w > 0 ? -1 : 1);
hmod=(h > 0 ? -1 : 1);
if(argc > 7 && *argv[8]) r = getint(argv[8], 0, 100);
if(argc > 9 && *argv[10]) c = getint(argv[10], 0, WHITE);
if(argc == 13) f = getint(argv[12], -1, WHITE);
if(w != 0 && h != 0) DrawRBox(x1, y1, x1 + w + wmod, y1 + h + hmod, r, c, f);
} else {
c = gui_fcolour; w = 1; // setup the defaults
if(argc > 7 && *argv[8]){
getargaddress(argv[8], &wptr, &wfptr, &nw);
if(nw == 1) w = getint(argv[8], 0, 100);
else if(nw>1) {
if(nw > 1 && nw < n) n=nw; //adjust the dimensionality
for(i=0;i<nw;i++){
w = (wfptr == NULL ? wptr[i] : (int)wfptr[i]);
if(w < 0 || w > 100) error("% is invalid (valid is % to %)", (int)w, 0, 100);
}
}
}
if(argc > 9 && *argv[10]) {
getargaddress(argv[10], &cptr, &cfptr, &nc);
if(nc == 1) c = getint(argv[10], 0, WHITE);
else if(nc>1) {
if(nc > 1 && nc < n) n=nc; //adjust the dimensionality
for(i=0;i<nc;i++){
c = (cfptr == NULL ? cptr[i] : (int)cfptr[i]);
if(c < 0 || c > WHITE) error("% is invalid (valid is % to %)", (int)c, 0, WHITE);
}
}
}
if(argc == 13){
getargaddress(argv[12], &fptr, &ffptr, &nf);
if(nf == 1) f = getint(argv[12], 0, WHITE);
else if(nf>1) {
if(nf > 1 && nf < n) n=nf; //adjust the dimensionality
for(i=0;i<nf;i++){
f = (ffptr == NULL ? fptr[i] : (int)ffptr[i]);
if(f < -1 || f > WHITE) error("% is invalid (valid is % to %)", (int)f, -1, WHITE);
}
}
}
for(i=0;i<n;i++){
x1 = (x1fptr==NULL ? x1ptr[i] : (int)x1fptr[i]);
y1 = (y1fptr==NULL ? y1ptr[i] : (int)y1fptr[i]);
wi = (wifptr==NULL ? wiptr[i] : (int)wifptr[i]);
h = (hfptr==NULL ? hptr[i] : (int)hfptr[i]);
wmod=(wi > 0 ? -1 : 1);
hmod=(h > 0 ? -1 : 1);
if(nw > 1) w = (wfptr==NULL ? wptr[i] : (int)wfptr[i]);
if(nc > 1) c = (cfptr==NULL ? cptr[i] : (int)cfptr[i]);
if(nf > 1) f = (ffptr==NULL ? fptr[i] : (int)ffptr[i]);
if(wi != 0 && h != 0) DrawRBox(x1, y1, x1 + wi + wmod, y1 + h + hmod, w, c, f);
}
}
if(Option.Refresh)Display_Refresh();
}
// this function positions the cursor within a PRINT command
void MIPS16 fun_at(void) {
char buf[27];
getargs(&ep, 5, (unsigned char *)",");
if(commandfunction(cmdtoken) != cmd_print) error("Invalid function");
// if((argc == 3 || argc == 5)) error("Incorrect number of arguments");
// AutoLineWrap = false;
CurrentX = getinteger(argv[0]);
if(argc>=3 && *argv[2])CurrentY = getinteger(argv[2]);
if(argc == 5) {
PrintPixelMode = getinteger(argv[4]);
if(PrintPixelMode < 0 || PrintPixelMode > 7) {
PrintPixelMode = 0;
error("Number out of bounds");
}
} else
PrintPixelMode = 0;
// BJR: VT100 set cursor location: <esc>[y;xf
// where x and y are ASCII string integers.
// Assumes overall font size of 6x12 pixels (480/80 x 432/36), including gaps between characters and lines
sprintf(buf, "\033[%d;%df", (int)CurrentY/(FontTable[gui_font >> 4][1] * (gui_font & 0b1111))+1, (int)CurrentX/(FontTable[gui_font >> 4][0] * (gui_font & 0b1111))+1);
SSPrintString(buf); // send it to the USB
if(PrintPixelMode==2 || PrintPixelMode==5)SSPrintString("\033[7m");
targ=T_STR;
sret = (unsigned char *)"\0"; // normally pointing sret to a string in flash is illegal
}
// these three functions were written by Peter Mather (matherp on the Back Shed forum)
// read the contents of a PIXEL out of screen memory
void fun_pixel(void) {
if((void *)ReadBuffer == (void *)DisplayNotSet) error("Invalid on this display");
int p;
int x, y;
getargs(&ep, 3, (unsigned char *)",");
if(argc != 3) error("Argument count");
x = getinteger(argv[0]);
y = getinteger(argv[2]);
ReadBuffer(x, y, x, y, (unsigned char *)&p);
iret = p & 0xFFFFFF;
targ = T_INT;
}
void cmd_triangle(void) { // thanks to Peter Mather (matherp on the Back Shed forum)
unsigned char *p;
if((p=checkstring(cmdline, (unsigned char *)"SAVE"))){
if((void *)ReadBuffer == (void *)DisplayNotSet) error("Invalid on this display");
cmd_ReadTriangle(p);
return;
}
if((p=checkstring(cmdline, (unsigned char *)"RESTORE"))){
if((void *)ReadBuffer == (void *)DisplayNotSet) error("Invalid on this display");
cmd_RestoreTriangle(p);
return;
}
int x1, y1, x2, y2, x3, y3, c=0, f=0, n=0,i, nc=0, nf=0;
long long int *x3ptr, *y3ptr, *x1ptr, *y1ptr, *x2ptr, *y2ptr, *fptr, *cptr;
MMFLOAT *x3fptr, *y3fptr, *x1fptr, *y1fptr, *x2fptr, *y2fptr, *ffptr, *cfptr;
getargs(&cmdline, 15,(unsigned char *)",");
if(Option.DISPLAY_TYPE == 0) error("Display not configured");
if(!(argc & 1) || argc < 11) error("Argument count");
getargaddress(argv[0], &x1ptr, &x1fptr, &n);
if(n != 1) {
int cn=n;
getargaddress(argv[2], &y1ptr, &y1fptr, &n);
if(n<cn)cn=n;
getargaddress(argv[4], &x2ptr, &x2fptr, &n);
if(n<cn)cn=n;
getargaddress(argv[6], &y2ptr, &y2fptr, &n);
if(n<cn)cn=n;
getargaddress(argv[8], &x3ptr, &x3fptr, &n);
if(n<cn)cn=n;
getargaddress(argv[10], &y3ptr, &y3fptr, &n);
if(n<cn)cn=n;
n=cn;
}
if(n == 1){
c = gui_fcolour; f = -1;
x1 = getinteger(argv[0]);
y1 = getinteger(argv[2]);
x2 = getinteger(argv[4]);
y2 = getinteger(argv[6]);
x3 = getinteger(argv[8]);
y3 = getinteger(argv[10]);
if(argc >= 13 && *argv[12]) c = getint(argv[12], BLACK, WHITE);
if(argc == 15) f = getint(argv[14], -1, WHITE);
DrawTriangle(x1, y1, x2, y2, x3, y3, c, f);
} else {
c = gui_fcolour; f = -1;
if(argc >= 13 && *argv[12]) {
getargaddress(argv[12], &cptr, &cfptr, &nc);
if(nc == 1) c = getint(argv[10], 0, WHITE);
else if(nc>1) {
if(nc > 1 && nc < n) n=nc; //adjust the dimensionality
for(i=0;i<nc;i++){
c = (cfptr == NULL ? cptr[i] : (int)cfptr[i]);
if(c < 0 || c > WHITE) error("% is invalid (valid is % to %)", (int)c, 0, WHITE);
}
}
}
if(argc == 15){
getargaddress(argv[14], &fptr, &ffptr, &nf);
if(nf == 1) f = getint(argv[14], -1, WHITE);
else if(nf>1) {
if(nf > 1 && nf < n) n=nf; //adjust the dimensionality
for(i=0;i<nf;i++){
f = (ffptr == NULL ? fptr[i] : (int)ffptr[i]);
if(f < -1 || f > WHITE) error("% is invalid (valid is % to %)", (int)f, -1, WHITE);
}
}
}
for(i=0;i<n;i++){
x1 = (x1fptr==NULL ? x1ptr[i] : (int)x1fptr[i]);
y1 = (y1fptr==NULL ? y1ptr[i] : (int)y1fptr[i]);
x2 = (x2fptr==NULL ? x2ptr[i] : (int)x2fptr[i]);
y2 = (y2fptr==NULL ? y2ptr[i] : (int)y2fptr[i]);
x3 = (x3fptr==NULL ? x3ptr[i] : (int)x3fptr[i]);
y3 = (y3fptr==NULL ? y3ptr[i] : (int)y3fptr[i]);
if(x1==x2 && x1==x3 && y1==y2 && y1==y3 && x1==-1 && y1==-1)return;
if(nc > 1) c = (cfptr==NULL ? cptr[i] : (int)cfptr[i]);
if(nf > 1) f = (ffptr==NULL ? fptr[i] : (int)ffptr[i]);
DrawTriangle(x1, y1, x2, y2, x3, y3, c, f);
}
}
if(Option.Refresh)Display_Refresh();
}
/*
* @cond
* The following section will be excluded from the documentation.
*/
char getnextuncompressednibble(char **s, int reset){
static int toggle=0;
if(reset){
toggle=reset-1;
return 0;
}
if(!toggle){
toggle ^=1;
return **s & 0x0f;
} else {
toggle ^=1;
char r=(**s & 0xf0)>>4;
(*s)++;
return r;
}
}
static inline char getnextnibble(char **fc, int reset){
static uint8_t available;
static char out;
if(reset){
available=0;
}
if(available==0){
available=**fc & 0xF; //number of identical pixels
out=(**fc)>>4;
(*fc)++;
}
if(!reset)available--;
return out;
}
void docompressed(char *fc,int x1, int y1, int w, int h, int8_t blank){
#ifndef PICOMITEVGA
if(!WriteBuf){ //direct to screen
if(blank==-1){
char tobuff[w/2], *to;
int ww=w;
int xx1=x1;
if(x1<0){
ww+=x1;
xx1=0;
}
if(x1+w>HRes){
ww=HRes-x1;
}
getnextnibble(&fc,1); //reset the decoder
for(int y=y1;y<y1+h;y++){
to=tobuff;
int otoggle=0;
for(int x=x1;x<x1+w;x++){
if(y<0 || y>=VRes){
getnextnibble(&fc,0);
continue;
}
if(x>=0 && x<HRes){
if(otoggle==0){
*to=getnextnibble(&fc,0);
otoggle ^=1;
} else {
*to|=(getnextnibble(&fc,0)<<4);
otoggle^=1;
to++;
}
} else getnextnibble(&fc,0);
}
if(ww>0 && xx1<HRes)copyframetoscreen((unsigned char *)tobuff,xx1, xx1+ww-1, y, y, 0);
}
} else {
char tobuff[w/2], *to;
getnextnibble(&fc,1); //reset the decoder
for(int y=y1;y<y1+h;y++){
int x=x1;
while(1){
to=tobuff;
int otoggle=0;
char c;
int ww=0;
int xx=-1;
while((c=getnextnibble(&fc,0))==blank){
x++;
if(x==x1+w)break;
}
if(x==x1+w)break; //nothing found so exit
*to=c;
otoggle ^=1;
xx=x;
x++;
ww=1;
if(xx!=x1+w-1){
while((c=getnextnibble(&fc,0))!=blank){
x++;
ww++;
if(otoggle==0){
*to=c;
otoggle ^=1;
} else {
*to|=(c<<4);
otoggle^=1;
to++;
}
if(x==x1+w)break;
}
}
x++;
if(xx+ww>HRes){
ww=HRes-xx;
}
if(xx>=0 && ww>0 && y>=0 && y<VRes)copyframetoscreen((unsigned char *)tobuff,xx, xx+ww-1, y, y, 0);
if(xx<0 && xx+ww>=0){
char *t=tobuff-(xx/2)-(xx&1);
ww+=xx;
if(ww>0)copyframetoscreen((unsigned char *)t,0, ww-1, y, y, xx&1);
}
if(x>=x1+w)break;
}
}
}
} else
#endif
if(x1 %2 == 0 && w % 2 == 0 && blank==-1){
char c, *to;
c=getnextnibble(&fc,1); //reset the decoder
for(int y=y1;y<y1+h;y++){
to=(char *)WriteBuf+y*(HRes>>1)+(x1>>1);
for(int x=x1;x<x1+w;x+=2){
c=getnextnibble(&fc,0);
c|=(getnextnibble(&fc,0)<<4);
if(y<0 || y>=VRes)continue;
if(x>=0 && x<HRes)*to=c;
to++;
}
}
} else {
int otoggle=0,itoggle=0; //input will always start on a byte boundary
char c,*to;
c=getnextnibble(&fc,1); //reset the decoder
for(int y=y1;y<y1+h;y++){ //loop though all of the output lines
to=(char *)WriteBuf+y*(HRes>>1)+(x1>>1); //get the byte that will start the output
if(x1 & 1)otoggle=1; // if x1 is odd then we will start on the high nibble
else otoggle=0;
for(int x=x1;x<x1+w;x++){
if(itoggle==0){
c=getnextnibble(&fc,0);
itoggle=1;
} else {
c=getnextnibble(&fc,0);
itoggle=0;
}
if(y<0 || y>=VRes)continue;
if(otoggle==0){
if(x>=0 && x<HRes){
if(c!=blank){
*to &= 0xF0;
*to |=c;
}
}
} else {
if(x>=0 && x<HRes){
if(c!=blank){
*to &=0x0f;
*to |= (c<<4);
}
}
to++;
}
otoggle^=1;
}
}
}
}
/* @endcond */
void cmd_blitmemory(void){
int x1, y1, w, h;
int8_t blank=-1;
getargs(&cmdline, 7, (unsigned char*)",");
if(argc<5)error("Syntax");
char *from=(char *)GetPeekAddr(argv[0]);
x1 = (int)getinteger(argv[2]);
y1 = (int)getinteger(argv[4]);
uint16_t *size=(uint16_t *)from;
w=(size[0] & 0x7FFF);
h=(size[1] & 0x7FFF);
from+=4;
if(argc==7)blank=getint(argv[6],-1,15);
if(size[0] & 0x8000 || size[1] & 0x8000) {
docompressed(from, x1, y1, w, h, blank);
} else {
#ifndef PICOMITEVGA
if(!WriteBuf){
if(blank==-1){
char *fc=from;
char tobuff[w/2], *to;
int ww=w;
int xx1=x1;
if(x1<0){
ww+=x1;
xx1=0;
}
if(x1+w>HRes){
ww=HRes-x1;
}
getnextuncompressednibble(&fc,1); //reset the decoder
for(int y=y1;y<y1+h;y++){
to=tobuff;
int otoggle=0;
for(int x=x1;x<x1+w;x++){
if(y<0 || y>=VRes){
getnextuncompressednibble(&fc,0);
continue;
}
if(x>=0 && x<HRes){
if(otoggle==0){
*to=getnextuncompressednibble(&fc,0);
otoggle ^=1;
} else {
*to|=(getnextuncompressednibble(&fc,0)<<4);
otoggle^=1;
to++;
}
} else getnextuncompressednibble(&fc,0);
}
if(ww>0 && xx1<HRes)copyframetoscreen((unsigned char *)tobuff,xx1, xx1+ww-1, y, y, 0);
}
} else {
char *fc=from;
char tobuff[w/2], *to;
getnextuncompressednibble(&fc,1); //reset the decoder
for(int y=y1;y<y1+h;y++){
int x=x1;
while(1){
to=tobuff;
int otoggle=0;
char c;
int ww=0;
int xx=-1;
while((c=getnextuncompressednibble(&fc,0))==blank){
x++;
if(x==x1+w)break;
}
if(x==x1+w)break; //nothing found so exit
*to=c;
otoggle ^=1;
xx=x;
x++;
ww=1;
if(xx!=x1+w-1){
while((c=getnextuncompressednibble(&fc,0))!=blank){
x++;
ww++;
if(otoggle==0){
*to=c;
otoggle ^=1;
} else {
*to|=(c<<4);
otoggle^=1;
to++;
}
if(x==x1+w)break;
}
}
x++;
if(xx+ww>HRes){
ww=HRes-xx;
}
if(xx>=0 && ww>0 && y>=0 && y<VRes)copyframetoscreen((unsigned char *)tobuff,xx, xx+ww-1, y, y, 0);
if(xx<0 && xx+ww>=0){
char *t=tobuff-(xx/2)-(xx&1);
ww+=xx;
if(ww>0)copyframetoscreen((unsigned char *)t,0, ww-1, y, y, xx&1);
}
if(x>=x1+w)break;
}
}
}
} else
#endif
if(x1 %2 == 0 && w % 2 == 0 && blank==-1){
char c, *to;
for(int y=y1;y<y1+h;y++){
to=(char *)WriteBuf+y*(HRes>>1)+(x1>>1);
for(int x=x1;x<x1+w;x+=2){
c=*from++;
if(y<0 || y>=VRes)continue;
if(x>=0 && x<HRes)*to=c;
to++;
}
}
} else {
int otoggle=0,itoggle=0; //input will always start on a byte boundary
char c,*to;
for(int y=y1;y<y1+h;y++){ //loop though all of the output lines
to=(char *)WriteBuf+y*(HRes>>1)+(x1>>1); //get the byte that will start the output
if(x1 & 1)otoggle=1; // if x1 is odd then we will start on the high nibble
else otoggle=0;
for(int x=x1;x<x1+w;x++){
if(itoggle==0){
c=*from & 0x0f;
itoggle=1;
} else {
c= *from >>4;
from++;
itoggle=0;
}
if(y<0 || y>=VRes)continue;
if(otoggle==0){
if(x>=0 && x<HRes){
if(c!=blank){
*to &= 0xF0;
*to |=c;
}
}
} else {
if(x>=0 && x<HRes){
if(c!=blank){
*to &=0x0f;
*to |= (c<<4);
}
}
to++;
}
otoggle^=1;
}
}
}
}
}
/*
* @cond
* The following section will be excluded from the documentation.
*/
int blitother(void){
int x1, y1, x2, y2, w, h;
unsigned char *p;
if ((p = checkstring(cmdline, (unsigned char*)"COMPRESSED"))) {
int8_t blank=-1;
getargs(&p, 7, (unsigned char*)",");
if(argc<5)error("Syntax");
char *fc=(char *)GetPeekAddr(argv[0]);
x1 = (int)getinteger(argv[2]);
y1 = (int)getinteger(argv[4]);
uint16_t *size=(uint16_t *)fc;
w=size[0];
h=size[1];
if(w>HRes || h>VRes)error("Invalid dimensions, w=%, h=%",w,h);
fc+=4;
if(argc==7)blank=getint(argv[6],-1,15);
docompressed(fc, x1, y1, w, h, blank);
return 1;
} else if ((p = checkstring(cmdline, (unsigned char*)"FRAMEBUFFER"))) {
int8_t blank=-1;
int otoggle=0,itoggle=0; //input will always start on a byte boundary
volatile unsigned char *s=NULL, *d=NULL;
getargs(&p, 17, (unsigned char*)",");
if(argc<15)error("Syntax");
if(checkstring(argv[0],(unsigned char*)"L"))s=LayerBuf;
else if(checkstring(argv[0],(unsigned char*)"F"))s=FrameBuf;
#ifdef PICOMITEVGA
else if(checkstring(argv[0],(unsigned char*)"N"))s=DisplayBuf;
#ifdef rp2350
else if(checkstring(argv[0],(unsigned char*)"T"))s=SecondLayer;
#endif
#else
else if(checkstring(argv[0],(unsigned char*)"N"))s=NULL;
#endif
else error("Syntax");
if(checkstring(argv[2],(unsigned char*)"L"))d=LayerBuf;
else if(checkstring(argv[2],(unsigned char*)"F"))d=FrameBuf;
#ifdef PICOMITEVGA
else if(checkstring(argv[2],(unsigned char*)"N"))d=DisplayBuf;
#ifdef rp2350
else if(checkstring(argv[2],(unsigned char*)"T"))d=SecondLayer;
#endif
#else
else if(checkstring(argv[2],(unsigned char*)"N"))d=NULL;
#endif
else error("Syntax");
if(s==d)error("Same framebuffer");
if(s==NULL && (void *)ReadBuffer == (void *)DisplayNotSet) error("Invalid on this display");
x1 = (int)getinteger(argv[4]);
y1 = (int)getinteger(argv[6]);
x2 = (int)getinteger(argv[8]);
y2 = (int)getinteger(argv[10]);
w = (int)getinteger(argv[12]);
h = (int)getinteger(argv[14]);
if(argc==17)blank=getint(argv[16],-1,15);
volatile unsigned char c,*to, *from;
if(d!=NULL && s!=NULL){
if(x1==0 && x2==0 && w==HRes && blank==-1){
s+=y1*HRes/2;
d+=y2*HRes/2;
memmove((void *)d,(void *)s,h*HRes/2);
} else {
for(int y=y1,toy=y2;y<y1+h;y++,toy++){ //loop though all of the output lines
from=s+y*(HRes>>1)+(x1>>1); //get the byte that will start the output
to=d+toy*(HRes>>1)+(x2>>1); //get the byte that will start the output
if(x1 & 1)itoggle=1; // if x1 is odd then we will start on the high nibble
else itoggle=0;
if(x2 & 1)otoggle=1; // if x1 is odd then we will start on the high nibble
else otoggle=0;
for(int x=x1,tox=x2;x<x1+w;x++,tox++){
if(itoggle==0){
if(tox>=0 && tox<HRes)c=*from & 0x0f;
else c=0;
itoggle=1;
} else {
if(tox>=0 && tox<HRes)c= *from >>4;
else c=0;
from++;
itoggle=0;
}
if(y<0 || y>=VRes)continue;
if(otoggle==0){
if(tox>=0 && tox<HRes){
if(c!=blank){
*to &= 0xF0;
*to |=c;
}
}
} else {
if(tox>=0 && tox<HRes){
if(c!=blank){
*to &=0x0f;
*to |= (c<<4);
}
}
to++;
}
otoggle^=1;
}
}
}
return 1;
}
#ifndef PICOMITEVGA
else if(s!=NULL){ //writing to a physical LCD display
if(x1==0 && x2==0 && w==HRes && blank==-1){
s+=y1*HRes/2;
copyframetoscreen((void *)s,0,HRes-1,y2,y2+h-1,0);
} else {
if(screen320 && Option.DISPLAY_TYPE!=SSD1963_4_16)x2*=2;
char tobuff[w/2], *to;
for(int y=y1,yo=y2;y<y1+h;y++,yo++){
char *fc=(char *)s+y*HRes/2+x1/2;
getnextuncompressednibble(&fc,1+(x1&1)); //reset the decoder
int x=x1;
while(1){
to=tobuff;
int otoggle=0;
char c;
int ww=0;
int xx=-1;
while((c=getnextuncompressednibble(&fc,0))==blank){
x++;
if(x==x1+w)break;
}
if(x==x1+w)break; //nothing found so exit
*to=c;
otoggle ^=1;
xx=x;
x++;
ww=1;
if(xx!=x1+w-1){
while((c=getnextuncompressednibble(&fc,0))!=blank){
x++;
ww++;
if(otoggle==0){
*to=c;
otoggle ^=1;
} else {
*to|=(c<<4);
otoggle^=1;
to++;
}
if(x==x1+w)break;
}
}
x++;
if(xx+ww>HRes){
ww=HRes-xx;
}
if(x2>=0 && ww>0 && yo>=0 && yo<VRes){
copyframetoscreen((unsigned char *)tobuff,x2, x2+ww-1, yo, yo, 0);
}
if(x2<0 && x2+ww>=0){
char *t=tobuff-((x2)/2)-(x2&1);
ww+=x2;
if(ww>0){
copyframetoscreen((unsigned char *)t,0, ww-1, yo, yo, x2&1);
}
}
if(x>=x1+w)break;
}
}
}
return 1;
} else if(d!=NULL){ //reading from a physical LCD display
union colourmap
{
char rgbbytes[4];
unsigned int rgb;
} cb;
unsigned char *rbuff=(unsigned char *)GetTempMemory(w*4);
char *from=GetTempMemory((w+1)/2);
for(int y=y1,toy=y2;y<y1+h;y++,toy++){ //loop though all of the output lines
ReadBuffer(x1,y,x1+w-1,y,rbuff);
uint8_t *p=rbuff;
char *pp=from;
for(int x=x1;x<x1+w;x++){
cb.rgbbytes[0]=*p++; //this order swaps the bytes to match the .BMP file
cb.rgbbytes[1]=*p++;
cb.rgbbytes[2]=*p++;
int fcolour = RGB121(cb.rgb);
if(x & 1){
*pp &=0x0F;
*pp |=(fcolour<<4);
pp++;
} else {
*pp &=0xF0;
*pp |= fcolour;
}
}
to=d+toy*(HRes>>1)+(x2>>1); //get the byte that will start the output
itoggle=0;
pp=from;
if(x2 & 1)otoggle=1; // if x1 is odd then we will start on the high nibble
else otoggle=0;
for(int x=x1,tox=x2;x<x1+w;x++,tox++){
if(itoggle==0){
if(tox>=0 && tox<HRes)c=*pp & 0x0f;
else c=0;
itoggle=1;
} else {
if(tox>=0 && tox<HRes)c= *pp >>4;
else c=0;
pp++;
itoggle=0;
}
if(y<0 || y>=VRes)continue;
if(otoggle==0){
if(tox>=0 && tox<HRes){
if(c!=blank){
*to &= 0xF0;
*to |=c;
}
}
} else {
if(tox>=0 && tox<HRes){
if(c!=blank){
*to &=0x0f;
*to |= (c<<4);
}
}
to++;
}
otoggle^=1;
}
}
return 1;
}
#endif
}
return 0;
}
/* @endcond */
void cmd_cls(void) {
if(Option.DISPLAY_TYPE == 0) error("Display not configured");
#ifdef GUICONTROLS
HideAllControls();
#endif
skipspace(cmdline);
if(!(*cmdline == 0 || *cmdline == '\'')){ //Colour specified
#ifdef PICOMITEVGA
if(DISPLAY_TYPE==SCREENMODE2 || DISPLAY_TYPE==SCREENMODE3 ){
int fc=getint(cmdline, 0, WHITE);
unsigned char fcolour = RGB121(fc);
fcolour|=(fcolour<<4);
memset((void *)WriteBuf,fcolour,ScreenSize);
} else {
ClearScreen(getint(cmdline, 0, WHITE));
}
#else
ClearScreen(getint(cmdline, 0, WHITE));
#endif
} else { //Default colour
#ifdef PICOMITEVGA
if((WriteBuf==LayerBuf && (DISPLAY_TYPE==SCREENMODE2 || DISPLAY_TYPE==SCREENMODE3 ) && LayerBuf!=DisplayBuf)
|| (WriteBuf==SecondLayer && (DISPLAY_TYPE==SCREENMODE2 || DISPLAY_TYPE==SCREENMODE3 ) && SecondLayer!=DisplayBuf)){
uint8_t colour=(WriteBuf==LayerBuf ? transparent|(transparent<<4) : transparents|(transparents<<4)) ;
memset((void *)WriteBuf,colour,HRes*VRes/2);
#ifdef HDMI
} else if(WriteBuf==LayerBuf && (DISPLAY_TYPE==SCREENMODE5 ) && LayerBuf!=DisplayBuf){
memset((void *)WriteBuf,transparent,HRes*VRes);
} else if((void *)WriteBuf==LayerBuf && (DISPLAY_TYPE==SCREENMODE4 ) && LayerBuf!=DisplayBuf){
uint16_t *p=(uint16_t *)WriteBuf;
for(int i=0;i<HRes*VRes;i++)*p++=RGBtransparent;
#endif
} else
#endif
ClearScreen(gui_bcolour);
}
CurrentX = CurrentY = 0;
if(Option.Refresh)Display_Refresh();
}
void fun_rgb(void) {
getargs(&ep, 5, (unsigned char *)",");
if(argc == 5)
iret = rgb(getint(argv[0], 0, 255), getint(argv[2], 0, 255), getint(argv[4], 0, 255));
else if(argc == 1) {
if(checkstring(argv[0], (unsigned char *)"WHITE")) iret = WHITE;
else if(checkstring(argv[0], (unsigned char *)"YELLOW")) iret = YELLOW;
else if(checkstring(argv[0], (unsigned char *)"LILAC")) iret = LILAC;
else if(checkstring(argv[0], (unsigned char *)"BROWN")) iret = BROWN;
else if(checkstring(argv[0], (unsigned char *)"FUCHSIA")) iret = FUCHSIA;
else if(checkstring(argv[0], (unsigned char *)"RUST")) iret = RUST;
else if(checkstring(argv[0], (unsigned char *)"MAGENTA")) iret = MAGENTA;
else if(checkstring(argv[0], (unsigned char *)"RED")) iret = RED;
else if(checkstring(argv[0], (unsigned char *)"CYAN")) iret = CYAN;
else if(checkstring(argv[0], (unsigned char *)"GREEN")) iret = GREEN;
else if(checkstring(argv[0], (unsigned char *)"CERULEAN"))iret = CERULEAN;
else if(checkstring(argv[0], (unsigned char *)"MIDGREEN"))iret = MIDGREEN;
else if(checkstring(argv[0], (unsigned char *)"COBALT")) iret = COBALT;
else if(checkstring(argv[0], (unsigned char *)"MYRTLE")) iret = MYRTLE;
else if(checkstring(argv[0], (unsigned char *)"BLUE")) iret = BLUE;
else if(checkstring(argv[0], (unsigned char *)"BLACK")) iret = BLACK;
else if(checkstring(argv[0], (unsigned char *)"GRAY")) iret = GRAY;
else if(checkstring(argv[0], (unsigned char *)"GREY")) iret = GRAY;
else if(checkstring(argv[0], (unsigned char *)"LIGHTGRAY")) iret = LITEGRAY;
else if(checkstring(argv[0], (unsigned char *)"LIGHTGREY")) iret = LITEGRAY;
else if(checkstring(argv[0], (unsigned char *)"ORANGE")) iret = ORANGE;
else if(checkstring(argv[0], (unsigned char *)"PINK")) iret = PINK;
else if(checkstring(argv[0], (unsigned char *)"GOLD")) iret = GOLD;
else if(checkstring(argv[0], (unsigned char *)"SALMON")) iret = SALMON;
else if(checkstring(argv[0], (unsigned char *)"BEIGE")) iret = BEIGE;
else error("Invalid colour: $", argv[0]);
} else
error("Syntax");
targ = T_INT;
}
/*
* @cond
* The following section will be excluded from the documentation.
*/
void fun_mmhres(void) {
iret = HRes;
targ = T_INT;
}
void fun_mmvres(void) {
iret = VRes;
targ = T_INT;
}
extern BYTE BDEC_bReadHeader(BMPDECODER *pBmpDec, int fnbr);
extern BYTE BMP_bDecode_memory(int x, int y, int xlen, int ylen, int fnbr, char *p);
void LIFOadd(int n) {
int i, j = 0;
for (i = 0; i < LIFOpointer; i++) {
if (LIFO[i] != n) {
LIFO[j] = LIFO[i];
j++;
}
}
LIFO[j] = n;
LIFOpointer = j + 1;
}
void LIFOremove(int n) {
int i, j = 0;
for (i = 0; i < LIFOpointer; i++) {
if (LIFO[i] != n) {
LIFO[j] = LIFO[i];
j++;
}
}
LIFOpointer = j;
}
void LIFOswap(int n, int m) {
int i;
for (i = 0; i < LIFOpointer; i++) {
if (LIFO[i] == n)LIFO[i] = m;
}
}
void zeroLIFOadd(int n) {
int i, j = 0;
for (i = 0; i < zeroLIFOpointer; i++) {
if (zeroLIFO[i] != n) {
zeroLIFO[j] = zeroLIFO[i];
j++;
}
}
zeroLIFO[j] = n;
zeroLIFOpointer = j + 1;
}
void zeroLIFOremove(int n) {
int i, j = 0;
for (i = 0; i < zeroLIFOpointer; i++) {
if (zeroLIFO[i] != n) {
zeroLIFO[j] = zeroLIFO[i];
j++;
}
}
zeroLIFOpointer = j;
}
void zeroLIFOswap(int n, int m) {
int i;
for (i = 0; i < zeroLIFOpointer; i++) {
if (zeroLIFO[i] == n)zeroLIFO[i] = m;
}
}
void MIPS16 closeallsprites(void) {
int i;
for (i = 0; i <= MAXBLITBUF; i++) {
if (i <= MAXLAYER)layer_in_use[i] = 0;
if (i) {
if (spritebuff[i].mymaster == -1) {
if (spritebuff[i].spritebuffptr != NULL) {
FreeMemory((unsigned char *)spritebuff[i].spritebuffptr);
spritebuff[i].spritebuffptr = NULL;
}
}
if (spritebuff[i].blitstoreptr != NULL) {
FreeMemory((unsigned char*)spritebuff[i].blitstoreptr);
spritebuff[i].blitstoreptr = NULL;
}
}
spritebuff[i].spritebuffptr = NULL;
spritebuff[i].blitstoreptr = NULL;
spritebuff[i].master = -1;
spritebuff[i].mymaster = -1;
spritebuff[i].x = 10000;
spritebuff[i].y = 10000;
spritebuff[i].w = 0;
spritebuff[i].h = 0;
spritebuff[i].next_x = 10000;
spritebuff[i].next_y = 10000;
spritebuff[i].layer = -1;
spritebuff[i].active = false;
spritebuff[i].edges = 0;
}
LIFOpointer = 0;
zeroLIFOpointer = 0;
sprites_in_use = 0;
hideall = 0;
}
void checklimits(int bnbr, int* n) {
int maxW = HRes;
int maxH = VRes;
spritebuff[bnbr].collisions[*n] = 0;
if (spritebuff[bnbr].x < 0) {
if (!(spritebuff[bnbr].edges & 1)) {
spritebuff[bnbr].edges |= 1;
spritebuff[bnbr].collisions[*n] = (char)0xF1;
(*n)++;
}
}
else spritebuff[bnbr].edges &= ~1;
if (spritebuff[bnbr].y < 0) {
if (!(spritebuff[bnbr].edges & 2)) {
spritebuff[bnbr].edges |= 2;
if (spritebuff[bnbr].collisions[*n] & 0xF0)spritebuff[bnbr].collisions[*n] |= 0xF2;
else {
spritebuff[bnbr].collisions[*n] = (char)0xF2;
(*n)++;
}
}
}
else spritebuff[bnbr].edges &= ~2;
if (spritebuff[bnbr].x + spritebuff[bnbr].w > maxW) {
if (!(spritebuff[bnbr].edges & 4)) {
spritebuff[bnbr].edges |= 4;
if (spritebuff[bnbr].collisions[*n] & 0xF0)spritebuff[bnbr].collisions[*n] |= 0xF4;
else {
spritebuff[bnbr].collisions[*n] = (char)0xF4;
(*n)++;
}
}
}
else spritebuff[bnbr].edges &= ~4;
if (spritebuff[bnbr].y + spritebuff[bnbr].h > maxH) {
if (!(spritebuff[bnbr].edges & 8)) {
spritebuff[bnbr].edges |= 8;
if (spritebuff[bnbr].collisions[*n] & 0xF0)spritebuff[bnbr].collisions[*n] |= 0xF8;
else {
spritebuff[bnbr].collisions[*n] = (char)0xF8;
(*n)++;
}
}
}
else spritebuff[bnbr].edges &= ~8;
}
void ProcessCollisions(int bnbr) {
int k, j = 1, n = 1, bcol = 1;
//We know that any collision is caused by movement of sprite bnbr
// a value of zero indicates that we are processing movement of layer 0 and any
// sprites on that layer
CollisionFound = false;
sprite_which_collided = -1;
uint64_t mask, mymask = (uint64_t)1 << ((uint64_t)bnbr - (uint64_t)1);
memset(spritebuff[0].collisions, 0, MAXCOLLISIONS);
if (bnbr != 0) { // a specific sprite has moved
memset(spritebuff[bnbr].collisions, 0, MAXCOLLISIONS); //clear our previous collisions
if (spritebuff[bnbr].layer != 0) {
if (layer_in_use[spritebuff[bnbr].layer] + layer_in_use[0] > 1) { //other sprites in this layer
for (k = 1; k <= MAXBLITBUF; k++) {
mask = (uint64_t)1 << ((uint64_t)k - (uint64_t)1);
if (!(spritebuff[k].active)) {
spritebuff[bnbr].lastcollisions &= ~mask;
continue;
}
if (k == bnbr) continue;
if (j == layer_in_use[spritebuff[bnbr].layer] + layer_in_use[0]) break; //nothing left to process
if ((spritebuff[k].layer == spritebuff[bnbr].layer || spritebuff[k].layer == 0)) {
j++;
if (!(spritebuff[k].x + spritebuff[k].w < spritebuff[bnbr].x ||
spritebuff[k].x > spritebuff[bnbr].x + spritebuff[bnbr].w ||
spritebuff[k].y + spritebuff[k].h < spritebuff[bnbr].y ||
spritebuff[k].y > spritebuff[bnbr].y + spritebuff[bnbr].h)) {
if (n < MAXCOLLISIONS && !(spritebuff[bnbr].lastcollisions & mask))spritebuff[bnbr].collisions[n++] = k;
spritebuff[bnbr].lastcollisions |= mask;
spritebuff[k].lastcollisions |= mymask;
}
else {
spritebuff[bnbr].lastcollisions &= ~mask;
spritebuff[k].lastcollisions &= ~mymask;
}
}
}
}
}
else {
for (k = 1; k <= MAXBLITBUF; k++) {
if (j == sprites_in_use) break; //nothing left to process
if (k == bnbr) continue;
mask = (uint64_t)1 << ((uint64_t)k - (uint64_t)1);
if (!(spritebuff[k].active)) {
spritebuff[bnbr].lastcollisions &= ~mask;
continue;
}
else j++;
if (!(spritebuff[k].x + spritebuff[k].w < spritebuff[bnbr].x ||
spritebuff[k].x > spritebuff[bnbr].x + spritebuff[bnbr].w ||
spritebuff[k].y + spritebuff[k].h < spritebuff[bnbr].y ||
spritebuff[k].y > spritebuff[bnbr].y + spritebuff[bnbr].h)) {
if (n < MAXCOLLISIONS && !(spritebuff[bnbr].lastcollisions & mask))spritebuff[bnbr].collisions[n++] = k;
spritebuff[bnbr].lastcollisions |= mask;
spritebuff[k].lastcollisions |= mymask;
}
else {
spritebuff[bnbr].lastcollisions &= ~mask;
spritebuff[k].lastcollisions &= ~mymask;
}
}
}
// now look for collisions with the edge of the screen
checklimits(bnbr, &n);
if (n > 1) {
CollisionFound = true;
sprite_which_collided = bnbr;
spritebuff[bnbr].collisions[0] = n - 1;
}
}
else { //the background layer has moved
j = 0;
for (k = 1; k <= MAXBLITBUF; k++) { //loop through all sprites
mask = (uint64_t)1 << ((uint64_t)k - (uint64_t)1);
n = 1;
int kk, jj = 1;
if (j == sprites_in_use) break; //nothing left to process
if (spritebuff[k].active) { //sprite found
memset(spritebuff[k].collisions, 0, MAXCOLLISIONS);
j++;
if (layer_in_use[spritebuff[k].layer] + layer_in_use[0] > 1) { //other sprites in this layer
for (kk = 1; kk <= MAXBLITBUF; kk++) {
if (kk == k) continue;
if (jj == layer_in_use[spritebuff[k].layer] + layer_in_use[0]) break; //nothing left to process
if ((spritebuff[kk].layer == spritebuff[k].layer || spritebuff[kk].layer == 0)) {
jj++;
if (!(spritebuff[kk].x + spritebuff[kk].w < spritebuff[k].x ||
spritebuff[kk].x > spritebuff[k].x + spritebuff[k].w ||
spritebuff[kk].y + spritebuff[kk].h < spritebuff[k].y ||
spritebuff[kk].y > spritebuff[k].y + spritebuff[k].h)) {
if (n < MAXCOLLISIONS && !(spritebuff[k].lastcollisions & mask))spritebuff[k].collisions[n++] = kk;
spritebuff[k].lastcollisions |= mask;
}
else {
spritebuff[k].lastcollisions &= ~mask;
}
}
}
}
checklimits(k, &n);
if (n > 1 && n < MAXCOLLISIONS && bcol < MAXCOLLISIONS) {
spritebuff[0].collisions[bcol] = k;
bcol++;
spritebuff[k].collisions[0] = n - 1;
}
}
}
if (bcol > 1) {
CollisionFound = true;
sprite_which_collided = 0;
spritebuff[0].collisions[0] = bcol - 1;
}
}
}
void blithide(int bnbr, int free) {
int w, h, x1, y1;
w = spritebuff[bnbr].w;
h = spritebuff[bnbr].h;
x1 = spritebuff[bnbr].x;
y1 = spritebuff[bnbr].y;
spritebuff[bnbr].active = 0;
DrawBufferFast(x1, y1, x1 + w - 1, y1 + h - 1, -1, (unsigned char *)spritebuff[bnbr].blitstoreptr);
}
void expandpixel(volatile unsigned char *ii, volatile unsigned char *oo, int n, int mode){
volatile unsigned char *o=oo,*i=ii;
int toggle=0;
if(mode==0){
while(n--){
if(toggle){
*o++=(*i++ >>4);
} else {
*o++=*i & 0xF;
}
toggle=!toggle;
}
} else {
while(n--){
*o++=(*i >> toggle++) & 0x1;
if(toggle==8){
i++;
toggle=0;
}
}
}
}
void contractpixel(volatile unsigned char *ii, volatile unsigned char *oo, int n, int mode){
int toggle=0;
volatile unsigned char *o=oo,*i=ii;
if(mode==0){
while(n--){
if(toggle){
*o++ |= (*i++ <<4);
} else {
*o= *i++ & 0xF;
}
toggle=!toggle;
}
} else {
while(n--){
if(toggle==0)*o=0;
*o|=(*i++ << toggle++);
if(toggle==8){
toggle=0;
o++;
}
}
}
}
void BlitShowBuffer(int bnbr, int x1, int y1, int mode) {
char* current;
int x, xx, y, yy, rotation, fullmode=mode;
mode &=7;
rotation = spritebuff[bnbr].rotation;
current = spritebuff[bnbr].blitstoreptr;
int w, h;
if (spritebuff[bnbr].spritebuffptr != NULL) {
w = spritebuff[bnbr].w;
h = spritebuff[bnbr].h;
if (!(mode == 0 || mode & 4) && spritebuff[bnbr].active) {
DrawBufferFast(spritebuff[bnbr].x, spritebuff[bnbr].y, spritebuff[bnbr].x + w - 1, spritebuff[bnbr].y + h - 1, -1, (unsigned char *)current);
}
spritebuff[bnbr].x = x1;
spritebuff[bnbr].y = y1;
if (!(mode == 2))ReadBufferFast(x1, y1, x1 + w - 1, y1 + h - 1, (unsigned char *)current);
// we now have the old screen image stored together with the coordinates
if(rotation){
unsigned char *d=GetTempMemory(w*h);
unsigned char *r=GetTempMemory((w*h+1)>>1);
expandpixel((unsigned char *)spritebuff[bnbr].spritebuffptr,d,w*h,0);
if(rotation & 1){ //swap left/write
for (y = 0; y < h; y++) {
for (x = 0,xx=w-1; x < (w>>1); x++,xx--) {
swap(d[y*w+x],d[y*w+xx]);
}
}
}
if(rotation & 2){
for(x=0;x<w;x++){
for(y=0,yy=h-1;y<(h>>1);y++,yy--){
swap(d[x+y*w],d[x+yy*w]);
}
}
}
contractpixel(d,r,w*h,0);
DrawBufferFast(x1, y1, x1 + w - 1, y1 + h - 1, ((fullmode & 8)==0 ? 0 : -1), (unsigned char *)r);
} else {
DrawBufferFast(x1, y1, x1 + w - 1, y1 + h - 1,((fullmode & 8)==0 ? 0 : -1) , (unsigned char *)spritebuff[bnbr].spritebuffptr);
}
if (!(mode & 4))spritebuff[bnbr].active = 1;
}
}
int sumlayer(void) {
int i, j = 0;
for (i = 0; i <= MAXLAYER; i++)j += layer_in_use[i];
return j;
}
void hidesafe(int bnbr) {
int found = 0;
int i;
for (i = LIFOpointer - 1; i >= 0; i--) {
if (LIFO[i] == bnbr) {
blithide(LIFO[i], 0);
found = i;
break;
}
blithide(LIFO[i], 0);
}
if (!found) {
for (i = zeroLIFOpointer - 1; i >= 0; i--) {
if (zeroLIFO[i] == bnbr) {
blithide(zeroLIFO[i], 0);
found = -i;
break;
}
blithide(zeroLIFO[i], 0);
}
}
sprites_in_use--;
layer_in_use[spritebuff[bnbr].layer]--;
spritebuff[bnbr].x = 10000;
spritebuff[bnbr].y = 10000;
if (spritebuff[bnbr].layer == 0)zeroLIFOremove(bnbr);
else LIFOremove(bnbr);
spritebuff[bnbr].layer = -1;
spritebuff[bnbr].next_x = 10000;
spritebuff[bnbr].next_y = 10000;
spritebuff[bnbr].lastcollisions = 0;
spritebuff[bnbr].edges = 0;
if (found < 0) {
found = -found;
for (i = found; i < zeroLIFOpointer; i++) {
BlitShowBuffer(zeroLIFO[i], spritebuff[zeroLIFO[i]].x, spritebuff[zeroLIFO[i]].y, 0);
}
for (i = 0; i < LIFOpointer; i++) {
BlitShowBuffer(LIFO[i], spritebuff[LIFO[i]].x, spritebuff[LIFO[i]].y, 0);
}
}
else {
for (i = found; i < LIFOpointer; i++) {
BlitShowBuffer(LIFO[i], spritebuff[LIFO[i]].x, spritebuff[LIFO[i]].y, 0);
}
}
}
void showsafe(int bnbr, int x, int y) {
int found = 0;
int i;
for (i = LIFOpointer - 1; i >= 0; i--) {
if (LIFO[i] == bnbr) {
blithide(LIFO[i], 0);
found = i;
break;
}
blithide(LIFO[i], 0);
}
if (!found) {
for (i = zeroLIFOpointer - 1; i >= 0; i--) {
if (zeroLIFO[i] == bnbr) {
blithide(zeroLIFO[i], 0);
found = -i;
break;
}
blithide(zeroLIFO[i], 0);
}
}
BlitShowBuffer(bnbr, x, y, 1);
if (found < 0) {
found = -found;
for (i = found + 1; i < zeroLIFOpointer; i++) {
BlitShowBuffer(zeroLIFO[i], spritebuff[zeroLIFO[i]].x, spritebuff[zeroLIFO[i]].y, 0);
}
for (i = 0; i < LIFOpointer; i++) {
BlitShowBuffer(LIFO[i], spritebuff[LIFO[i]].x, spritebuff[LIFO[i]].y, 0);
}
}
else {
for (i = found + 1; i < LIFOpointer; i++) {
BlitShowBuffer(LIFO[i], spritebuff[LIFO[i]].x, spritebuff[LIFO[i]].y, 0);
}
}
}
void MIPS16 loadsprite(unsigned char* p) {
int fnbr, width, number, height = 0, newsprite = 1, startsprite = 1, bnbr, lc, i, toggle=0;;
char *q, *fname;
unsigned char buff[256], *z;
uint32_t data;
getargs(&p, 5, (unsigned char *)",");
int mode=0;
fnbr = FindFreeFileNbr();
if(!InitSDCard()) return;
fname = (char*)getFstring(argv[0]);
if (argc >= 3 && *argv[2])startsprite = (int)getint(argv[2], 1, 64);
if(argc==5)mode=getint(argv[4],0,1);
if(strchr(fname, '.') == NULL) strcat(fname, ".spr");
if (!BasicFileOpen(fname, fnbr, FA_READ)) error((char *)"File not found");
MMgetline(fnbr, (char*)buff); // get the input line
while (buff[0] == 39)MMgetline(fnbr, (char*)buff);
z=buff;
{
getargs(&z,5,(unsigned char *)", ");
width=getinteger(argv[0]);
number=getinteger(argv[2]);
if(argc==5)height=getinteger(argv[4]);
if (height == 0)height = width;
bnbr = startsprite;
if (number + startsprite > MAXBLITBUF) {
FileClose(fnbr);
error((char *)"Maximum of % sprites",MAXBLITBUF);
}
while (!MMfeof(fnbr) && bnbr <= number + startsprite) { // while waiting for the end of file
if (newsprite) {
newsprite = 0;
if (spritebuff[bnbr].spritebuffptr == NULL)spritebuff[bnbr].spritebuffptr = (char *)GetMemory((width * height + 1)>>1);
if (spritebuff[bnbr].blitstoreptr == NULL)spritebuff[bnbr].blitstoreptr = (char*)GetMemory((width * height + 1)>>1);
spritebuff[bnbr].w = width;
spritebuff[bnbr].h = height;
spritebuff[bnbr].master = 0;
spritebuff[bnbr].mymaster = -1;
spritebuff[bnbr].x = 10000;
spritebuff[bnbr].y = 10000;
spritebuff[bnbr].layer = -1;
spritebuff[bnbr].next_x = 10000;
spritebuff[bnbr].next_y = 10000;
spritebuff[bnbr].active = false;
spritebuff[bnbr].lastcollisions = 0;
spritebuff[bnbr].edges = 0;
q = spritebuff[bnbr].spritebuffptr;
lc = height;
}
while (lc--) {
MMgetline(fnbr, (char*)buff); // get the input line
while (buff[0] == 39)MMgetline(fnbr, (char*)buff);
if ((int)strlen((char*)buff) < width)memset(&buff[strlen((char*)buff)], 32, width - strlen((char*)buff));
for (i = 0; i < width; i++) {
if(mode){
if (buff[i] == ' ')data = 0;
else if (buff[i] == '0')data = BLACK;
else if (buff[i] == '1')data = BLUE;
else if (buff[i] == '2')data = MYRTLE;
else if (buff[i] == '3')data = COBALT;
else if (buff[i] == '4')data = MIDGREEN;
else if (buff[i] == '5')data = CERULEAN;
else if (buff[i] == '6')data = GREEN;
else if (buff[i] == '7')data = CYAN;
else if (buff[i] == '8')data = RED;
else if (buff[i] == '9')data = MAGENTA;
else if (buff[i] == 'A' || buff[i] == 'a')data = RUST;
else if (buff[i] == 'B' || buff[i] == 'b')data = FUCHSIA;
else if (buff[i] == 'C' || buff[i] == 'c')data = BROWN;
else if (buff[i] == 'D' || buff[i] == 'd')data = LILAC;
else if (buff[i] == 'E' || buff[i] == 'e')data = YELLOW;
else if (buff[i] == 'F' || buff[i] == 'f')data = WHITE;
else data = 0;
} else {
if (buff[i] == ' ')data = 0;
else if (buff[i] == '0')data = BLACK;
else if (buff[i] == '1')data = BLUE;
else if (buff[i] == '2')data = GREEN;
else if (buff[i] == '3')data = CYAN;
else if (buff[i] == '4')data = RED;
else if (buff[i] == '5')data = MAGENTA;
else if (buff[i] == '6')data = YELLOW;
else if (buff[i] == '7')data = WHITE;
else if (buff[i] == '8')data = MYRTLE;
else if (buff[i] == '9')data = COBALT;
else if (buff[i] == 'A' || buff[i] == 'a')data = MIDGREEN;
else if (buff[i] == 'B' || buff[i] == 'b')data = CERULEAN;
else if (buff[i] == 'C' || buff[i] == 'c')data = RUST;
else if (buff[i] == 'D' || buff[i] == 'd')data = FUCHSIA;
else if (buff[i] == 'E' || buff[i] == 'e')data = BROWN;
else if (buff[i] == 'F' || buff[i] == 'f')data = LILAC;
else data = 0;
}
if(toggle){
*q++ |= (RGB121(data)<<4);
} else {
*q=RGB121(data);
}
toggle=!toggle;
}
}
bnbr++;
newsprite = 1;
}
FileClose(fnbr);
}
}
void MIPS16 loadarray(unsigned char* p) {
int bnbr, w, h, size, i, toggle=0;
int maxH = VRes;
int maxW =HRes;
MMFLOAT* a3float = NULL;
int64_t* a3int = NULL;
char* q;
// uint16_t* qq;
// uint32_t* qqq;
getargs(&p, 7, (unsigned char *)",");
if (*argv[0] == '#') argv[0]++;
bnbr = (int)getint(argv[0], 1, MAXBLITBUF);
if (spritebuff[bnbr].spritebuffptr == NULL) {
w = (int)getint(argv[2], 1, maxW);
h = (int)getint(argv[4], 1, maxH);
size=parsenumberarray(argv[6],&a3float,&a3int,4,1,NULL,true)-1;
if (size < w * h - 1)error((char *)"Array Dimensions");
spritebuff[bnbr].spritebuffptr = (char *)GetMemory((w * h + 1)>>1);
spritebuff[bnbr].blitstoreptr = (char *)GetMemory((w * h + 1)>>1);
spritebuff[bnbr].w = w;
spritebuff[bnbr].h = h;
spritebuff[bnbr].master = 0;
spritebuff[bnbr].mymaster = -1;
spritebuff[bnbr].x = 10000;
spritebuff[bnbr].y = 10000;
spritebuff[bnbr].layer = -1;
spritebuff[bnbr].next_x = 10000;
spritebuff[bnbr].next_y = 10000;
spritebuff[bnbr].active = false;
spritebuff[bnbr].lastcollisions = 0;
spritebuff[bnbr].edges = 0;
q = spritebuff[bnbr].spritebuffptr;
int c;
for (i = 0; i < w * h; i++) {
if (a3float)c = (int)a3float[i];
else c = (int)a3int[i];
if(toggle){
*q++ |= (RGB121(c)<<4);
} else {
*q=RGB121(c);
}
toggle=!toggle;
}
}
else error((char *)"Buffer already in use");
}
void ScrollBufferH(int pixels) {
if (!pixels)return;
volatile uint8_t *s, *d, *l, *ss, *dd;
int y;
if(HRes==320 && !(pixels & 1)){
if (pixels > 0) {
for (y = 0; y < VRes; y++) {
s = (((y * HRes )>>1) + WriteBuf);
d = s + (pixels>>1);
memmove((void *)d,(void *)s,160-(pixels>>1));
}
} else {
pixels = -pixels;
for (y = 0; y < VRes; y++) {
s = (((y * HRes )>>1) + WriteBuf);
d=s;
s+=(pixels>>1);
memmove((void *)d,(void *)s,160-(pixels>>1));
}
}
} else {
ss=GetTempMemory(HRes);
dd=GetTempMemory(HRes);
if (pixels > 0) {
for (y = 0; y < VRes; y++) {
l = (((y * HRes )>>(HRes==320?1:3)) + WriteBuf);
s=ss;
d=dd + pixels;
expandpixel(l,s,HRes,(HRes==320?0:1));
memcpy((void *)d,(void *)s,(HRes - pixels));
contractpixel(dd,l,HRes,(HRes==320?0:1));
}
} else {
pixels = -pixels;
for (y = 0; y < VRes; y++) {
l = (((y * HRes )>>(HRes==320?1:3)) + WriteBuf);
s=ss;
d=dd;
expandpixel(l,s,HRes,(HRes==320?0:1));
s += pixels;
memcpy((void *)d,(void *)s,(HRes - pixels));
contractpixel(d,l,HRes,(HRes==320?0:1));
}
}
}
}
void ScrollBufferV(int lines, int blank) {
uint8_t* s, * d;
int y, yy;
if(HRes==320){
int n = (HRes>>1);
if (lines > 0) {
for (y = 0; y < VRes - lines; y++) {
yy = y + lines;
d = (uint8_t*)(((y * HRes )>>1) + WriteBuf);
s = (uint8_t*)(((yy * HRes )>>1) + WriteBuf);
memcpy(d, s, n);
}
if (blank) {
DrawRectangle(0, VRes - lines, HRes - 1, VRes - 1, gui_bcolour); // erase the line to be scrolled off
}
}
else if (lines < 0) {
lines = -lines;
for (y = VRes - 1; y >= lines; y--) {
yy = y - lines;
d = (uint8_t*)(((y * HRes)>>1) + WriteBuf);
s = (uint8_t*)(((yy * HRes )>>1) + WriteBuf);
memcpy(d, s, n);
}
if (blank)DrawRectangle(0, 0, HRes - 1, lines - 1, gui_bcolour); // erase the line to be scrolled off
}
} else {
int n = (HRes>>3);
if (lines > 0) {
for (y = 0; y < VRes - lines; y++) {
yy = y + lines;
d = (uint8_t*)(((y * HRes )>>3) + WriteBuf);
s = (uint8_t*)(((yy * HRes )>>3) + WriteBuf);
memcpy(d, s, n);
}
if (blank) {
DrawRectangle(0, VRes - lines, HRes - 1, VRes - 1, gui_bcolour); // erase the line to be scrolled off
}
}
else if (lines < 0) {
lines = -lines;
for (y = VRes - 1; y >= lines; y--) {
yy = y - lines;
d = (uint8_t*)(((y * HRes)>>3) + WriteBuf);
s = (uint8_t*)(((yy * HRes )>>3) + WriteBuf);
memcpy(d, s, n);
}
if (blank)DrawRectangle(0, 0, HRes - 1, lines - 1, gui_bcolour); // erase the line to be scrolled off
}
}
}
/* @endcond */
void cmd_sprite(void) {
int x1, y1, w, h, bnbr;
unsigned char *p;
int maxW = HRes;
int maxH = VRes;
int newb = 0;
#ifndef PICOMITEVGA
if(WriteBuf==NULL)error("Not available on physical display");
#endif
if(Option.DISPLAY_TYPE == 0) error("Display not configured");
if(DISPLAY_TYPE==SCREENMODE4 || DISPLAY_TYPE==SCREENMODE5 )error("Not available for this display mode");
if ((p = checkstring(cmdline, (unsigned char *)"SHOW SAFE"))) {
int layer, mode=1;
getargs(&p, 11, (unsigned char*)",");
if (!(argc == 7 || argc == 9 || argc == 11)) error((char *)"Syntax");
if (hideall)error((char *)"Sprites are hidden");
if (*argv[0] == '#') argv[0]++;
bnbr = (int)getint(argv[0], 1, MAXBLITBUF); // get the number
if(spritebuff[bnbr].h==9999)error("Invalid buffer");
if (spritebuff[bnbr].spritebuffptr != NULL) {
x1 = (int)getint(argv[2], -spritebuff[bnbr].w + 1, maxW - 1);
y1 = (int)getint(argv[4], -spritebuff[bnbr].h + 1, maxH - 1);
layer = (int)getint(argv[6], 0, MAXLAYER);
if (argc >= 9 && *argv[8])spritebuff[bnbr].rotation = (char)getint(argv[8], 0, 7);
else spritebuff[bnbr].rotation = 0;
if(spritebuff[bnbr].rotation>3){
mode |=8;
spritebuff[bnbr].rotation&=3;
}
if (argc == 11 && *argv[10]) {
newb = (int)getint(argv[10], 0, 1);
}
// q = spritebuff[bnbr].spritebuffptr;
w = spritebuff[bnbr].w;
h = spritebuff[bnbr].h;
if (spritebuff[bnbr].active) {
if (newb) {
hidesafe(bnbr);
spritebuff[bnbr].layer = layer;
layer_in_use[spritebuff[bnbr].layer]++;
if (spritebuff[bnbr].layer == 0) zeroLIFOadd(bnbr);
else LIFOadd(bnbr);
sprites_in_use++;
BlitShowBuffer(bnbr, x1, y1, mode);
}
else {
showsafe(bnbr, x1, y1);
}
}
else {
spritebuff[bnbr].layer = layer;
layer_in_use[spritebuff[bnbr].layer]++;
if (spritebuff[bnbr].layer == 0) zeroLIFOadd(bnbr);
else LIFOadd(bnbr);
sprites_in_use++;
BlitShowBuffer(bnbr, x1, y1, mode);
}
ProcessCollisions(bnbr);
if (sprites_in_use != LIFOpointer + zeroLIFOpointer || sprites_in_use != sumlayer())error((char *)"sprite internal error");
}
else error((char *)"Buffer not in use");
} else if ((p = checkstring(cmdline, (unsigned char*)"SHOW"))) {
int layer, mode=1;
getargs(&p, 9, (unsigned char*)",");
if (!(argc == 7 || argc == 9)) error((char *)"Syntax");
if (hideall)error((char *)"Sprites are hidden");
if (*argv[0] == '#') argv[0]++;
bnbr = (int)getint(argv[0], 1, MAXBLITBUF); // get the number
if(spritebuff[bnbr].h==9999)error("Invalid buffer");
if (spritebuff[bnbr].spritebuffptr != NULL) {
x1 = (int)getint(argv[2], -spritebuff[bnbr].w + 1, maxW - 1);
y1 = (int)getint(argv[4], -spritebuff[bnbr].h + 1, maxH - 1);
layer = (int)getint(argv[6], 0, MAXLAYER);
if (argc == 9)spritebuff[bnbr].rotation = (int)getint(argv[8], 0, 7);
else spritebuff[bnbr].rotation = 0;
if(spritebuff[bnbr].rotation>3){
mode |=8;
spritebuff[bnbr].rotation&=3;
}
w = spritebuff[bnbr].w;
h = spritebuff[bnbr].h;
if (spritebuff[bnbr].active) {
layer_in_use[spritebuff[bnbr].layer]--;
if (spritebuff[bnbr].layer == 0)zeroLIFOremove(bnbr);
else LIFOremove(bnbr);
sprites_in_use--;
}
spritebuff[bnbr].layer = layer;
layer_in_use[spritebuff[bnbr].layer]++;
if (spritebuff[bnbr].layer == 0) zeroLIFOadd(bnbr);
else LIFOadd(bnbr);
sprites_in_use++;
// int cursorhidden = 0;
BlitShowBuffer(bnbr, x1, y1, mode);
ProcessCollisions(bnbr);
if (sprites_in_use != LIFOpointer + zeroLIFOpointer || sprites_in_use != sumlayer())error((char *)"sprite internal error");
}
else error((char *)"Buffer not in use");
}
else if ((p = checkstring(cmdline, (unsigned char*)"HIDE ALL"))) {
if (hideall)error((char *)"Sprites are hidden");
int i;
// int cursorhidden = 0;
for (i = LIFOpointer - 1; i >= 0; i--) {
blithide(LIFO[i], 0);
}
for (i = zeroLIFOpointer - 1; i >= 0; i--) {
blithide(zeroLIFO[i], 0);
}
hideall = 1;
}
else if ((p = checkstring(cmdline, (unsigned char*)"RESTORE"))) {
if (!hideall)error((char *)"Sprites are not hidden");
int i;
// int cursorhidden = 0;
for (i = 0; i < zeroLIFOpointer; i++) {
BlitShowBuffer(zeroLIFO[i], spritebuff[zeroLIFO[i]].x, spritebuff[zeroLIFO[i]].y, 0);
}
for (i = 0; i < LIFOpointer; i++) {
if (spritebuff[LIFO[i]].next_x != 10000) {
spritebuff[LIFO[i]].x = spritebuff[LIFO[i]].next_x;
spritebuff[LIFO[i]].next_x = 10000;
}
if (spritebuff[LIFO[i]].next_y != 10000) {
spritebuff[LIFO[i]].y = spritebuff[LIFO[i]].next_y;
spritebuff[LIFO[i]].next_y = 10000;
}
BlitShowBuffer(LIFO[i], spritebuff[LIFO[i]].x, spritebuff[LIFO[i]].y, 0);
}
hideall = 0;
ProcessCollisions(0);
}
else if ((p = checkstring(cmdline, (unsigned char*)"HIDE SAFE"))) {
getargs(&p, 1, (unsigned char*)",");
if (sprites_in_use != LIFOpointer + zeroLIFOpointer || sprites_in_use != sumlayer())error((char *)"sprite internal error");
if (argc != 1) error((char *)"Syntax");
if (*argv[0] == '#') argv[0]++;
bnbr = (int)getint(argv[0], 1, MAXBLITBUF); // get the number
if (hideall)error((char *)"Sprites are hidden");
if (spritebuff[bnbr].spritebuffptr != NULL) {
if (spritebuff[bnbr].active) {
// int cursorhidden = 0;
hidesafe(bnbr);
if (sprites_in_use != LIFOpointer + zeroLIFOpointer || sprites_in_use != sumlayer())error((char *)"sprite internal error");
}
else error((char *)"Not Showing");
}
else error((char *)"Buffer not in use");
}
else if ((p = checkstring(cmdline, (unsigned char*)"HIDE"))) {
getargs(&p, 1, (unsigned char*)",");
if (argc != 1) error((char *)"Syntax");
if (*argv[0] == '#') argv[0]++;
bnbr = (int)getint(argv[0], 1, MAXBLITBUF); // get the number
if (spritebuff[bnbr].spritebuffptr != NULL) {
if (spritebuff[bnbr].active) {
sprites_in_use--;
// int cursorhidden = 0;
blithide(bnbr, 0);
layer_in_use[spritebuff[bnbr].layer]--;
spritebuff[bnbr].x = 10000;
spritebuff[bnbr].y = 10000;
if (spritebuff[bnbr].layer == 0)zeroLIFOremove(bnbr);
else LIFOremove(bnbr);
spritebuff[bnbr].layer = -1;
spritebuff[bnbr].next_x = 10000;
spritebuff[bnbr].next_y = 10000;
spritebuff[bnbr].lastcollisions = 0;
spritebuff[bnbr].edges = 0;
}
else error((char *)"Not Showing");
}
else error((char *)"Buffer not in use");
if (sprites_in_use != LIFOpointer + zeroLIFOpointer || sprites_in_use != sumlayer())error((char *)"sprite internal error");
//
}
else if ((p = checkstring(cmdline, (unsigned char*)"SWAP"))) {
int rbnbr=0, mode=2;
int64_t master;
signed char mymaster;
getargs(&p, 5, (unsigned char*)",");
if (argc < 3) error((char *)"Syntax");
if (hideall)error((char *)"Sprites are hidden");
if (*argv[0] == '#') argv[0]++;
bnbr = (int)getint(argv[0], 1, MAXBLITBUF); // get the number
if (*argv[2] == '#') argv[0]++;
rbnbr = (int)getint(argv[2], 1, MAXBLITBUF); // get the number
if (spritebuff[bnbr].spritebuffptr == NULL || spritebuff[bnbr].active == false) error((char *)"Original buffer not displayed");
if (!spritebuff[bnbr].active)error((char *)"Original buffer not displayed");
// if (spritebuff[bnbr].master == -1)error((char *)"Can't swap a copy");
if (spritebuff[rbnbr].active) error((char *)"New buffer already displayed");
// if (spritebuff[rbnbr].master == -1)error((char *)"Can't swap a copy");
if (!(spritebuff[rbnbr].w == spritebuff[bnbr].w && spritebuff[rbnbr].h == spritebuff[bnbr].h)) error((char *)"Size mismatch");
// copy the relevant data
master=spritebuff[rbnbr].master;
mymaster=spritebuff[rbnbr].mymaster;
spritebuff[rbnbr].master=spritebuff[bnbr].master;
spritebuff[rbnbr].mymaster=spritebuff[bnbr].mymaster;
spritebuff[rbnbr].blitstoreptr = spritebuff[bnbr].blitstoreptr;
spritebuff[rbnbr].x = spritebuff[bnbr].x;
spritebuff[rbnbr].y = spritebuff[bnbr].y;
spritebuff[rbnbr].layer = spritebuff[bnbr].layer;
spritebuff[rbnbr].lastcollisions = spritebuff[bnbr].lastcollisions;
if (spritebuff[rbnbr].layer == 0)zeroLIFOswap(bnbr, rbnbr);
else LIFOswap(bnbr, rbnbr);
// "Hide" the old sprite
spritebuff[bnbr].master=master;
spritebuff[bnbr].mymaster=mymaster;
spritebuff[bnbr].x = 10000;
spritebuff[bnbr].y = 10000;
spritebuff[bnbr].layer = -1;
spritebuff[bnbr].next_x = 10000;
spritebuff[bnbr].next_y = 10000;
spritebuff[bnbr].active = 0;
spritebuff[bnbr].lastcollisions = 0;
if (argc == 5)spritebuff[rbnbr].rotation = (int)getint(argv[4], 0, 7);
else spritebuff[bnbr].rotation = 0;
if(spritebuff[rbnbr].rotation>3){
mode |=8;
spritebuff[rbnbr].rotation&=3;
}
BlitShowBuffer(rbnbr, spritebuff[rbnbr].x, spritebuff[rbnbr].y, mode);
if (sprites_in_use != LIFOpointer + zeroLIFOpointer || sprites_in_use != sumlayer())error((char *)"sprite internal error");
}
else if ((p = checkstring(cmdline, (unsigned char*)"READ"))) {
getargs(&p, 11, (unsigned char*)",");
if (!(argc == 9)) error((char *)"Syntax");
if (*argv[0] == '#') argv[0]++;
bnbr = (int)getint(argv[0], 1, MAXBLITBUF); // get the number
x1 = (int)getinteger(argv[2]);
y1 = (int)getinteger(argv[4]);
w = (int)getinteger(argv[6]);
h = (int)getinteger(argv[8]);
if (w < 1 || h < 1) return;
if (spritebuff[bnbr].spritebuffptr == NULL) {
spritebuff[bnbr].spritebuffptr = (char *)GetMemory((w * h +1)>>1 );
spritebuff[bnbr].blitstoreptr = (char*)GetMemory((w * h +1)>>1 );
spritebuff[bnbr].w = w;
spritebuff[bnbr].h = h;
spritebuff[bnbr].master = 0;
spritebuff[bnbr].mymaster = -1;
spritebuff[bnbr].x = 10000;
spritebuff[bnbr].y = 10000;
spritebuff[bnbr].layer = -1;
spritebuff[bnbr].next_x = 10000;
spritebuff[bnbr].next_y = 10000;
spritebuff[bnbr].active = false;
spritebuff[bnbr].lastcollisions = 0;
spritebuff[bnbr].edges = 0;
}
else {
if (spritebuff[bnbr].mymaster != -1) error((char *)"Can't read into a copy", bnbr);
if (spritebuff[bnbr].master > 0) error((char *)"Copies exist", bnbr);
if (!(spritebuff[bnbr].w == w && spritebuff[bnbr].h == h))error((char *)"Existing buffer is incorrect size");
}
ReadBufferFast(x1, y1, x1 + w - 1, y1 + h - 1,(unsigned char *)spritebuff[bnbr].spritebuffptr);
}
else if ((p = checkstring(cmdline, (unsigned char*)"COPY"))) {
int cpy, nbr, c1, n1;
getargs(&p, 5, (unsigned char*)",");
if (argc != 5) error((char *)"Syntax");
if (*argv[0] == '#') argv[0]++;
bnbr = (int)getint(argv[0], 1, MAXBLITBUF); // get the number
if (spritebuff[bnbr].spritebuffptr != NULL) {
if (*argv[2] == '#') argv[2]++;
c1 = cpy = (int)getint(argv[2], 1, MAXBLITBUF);
n1 = nbr = (int)getint(argv[4], 1, MAXBLITBUF - 1);
while (n1) {
if (spritebuff[c1].spritebuffptr != NULL)error((char *)"Buffer already in use %", c1);
if (spritebuff[bnbr].master == -1)error((char *)"Can't copy a copy");;
n1--;
c1++;
}
while (nbr) {
spritebuff[cpy].spritebuffptr = spritebuff[bnbr].spritebuffptr;
spritebuff[cpy].w = spritebuff[bnbr].w;
spritebuff[cpy].h = spritebuff[bnbr].h;
spritebuff[cpy].blitstoreptr = (char *)GetMemory((spritebuff[cpy].w * spritebuff[cpy].h +1)>>1);
spritebuff[cpy].x = 10000;
spritebuff[cpy].y = 10000;
spritebuff[cpy].next_x = 10000;
spritebuff[cpy].next_y = 10000;
spritebuff[cpy].layer = -1;
spritebuff[cpy].mymaster = bnbr;
spritebuff[cpy].master = -1;
spritebuff[cpy].edges = 0;
spritebuff[bnbr].master |= ((int64_t)1 << (int64_t)cpy);
spritebuff[bnbr].lastcollisions = 0;
spritebuff[cpy].active = false;
nbr--;
cpy++;
}
}
else error((char *)"Buffer not in use");
} else if ((p = checkstring(cmdline, (unsigned char*)"LOADARRAY"))) {
loadarray(p);
} else if ((p = checkstring(cmdline, (unsigned char *)"LOAD"))) {
loadsprite(p);
return;
} else if ((p = checkstring(cmdline, (unsigned char*)"INTERRUPT"))) {
getargs(&p, 1, (unsigned char*)",");
COLLISIONInterrupt = (char*)GetIntAddress(argv[0]); // get the interrupt location
InterruptUsed = true;
return;
}
else if ((p = checkstring(cmdline, (unsigned char*)"NOINTERRUPT"))) {
COLLISIONInterrupt = NULL; // get the interrupt location
return;
}
else if ((p = checkstring(cmdline, (unsigned char*)"CLOSE ALL"))) {
closeallsprites();
} else if ((p = checkstring(cmdline, (unsigned char*)"CLOSE"))) {
getargs(&p, 1, (unsigned char*)",");
if (hideall)error((char *)"Sprites are hidden");
if (*argv[0] == '#') argv[0]++;
bnbr = (int)getint(argv[0], 1, MAXBLITBUF);
if (spritebuff[bnbr].master > 0) error((char *)"Copies still open");
if (spritebuff[bnbr].spritebuffptr != NULL) {
if (spritebuff[bnbr].active) {
blithide(bnbr, 1);
if (spritebuff[bnbr].layer == 0)zeroLIFOremove(bnbr);
else LIFOremove(bnbr);
layer_in_use[spritebuff[bnbr].layer]--;
sprites_in_use--;
}
if (spritebuff[bnbr].mymaster == -1)FreeMemorySafe((void**)&spritebuff[bnbr].spritebuffptr);
else spritebuff[spritebuff[bnbr].mymaster].master &= ~(1 << bnbr);
FreeMemorySafe((void**)&spritebuff[bnbr].blitstoreptr);
spritebuff[bnbr].spritebuffptr = NULL;
spritebuff[bnbr].blitstoreptr = NULL;
spritebuff[bnbr].master = -1;
spritebuff[bnbr].mymaster = -1;
spritebuff[bnbr].x = 10000;
spritebuff[bnbr].y = 10000;
spritebuff[bnbr].w = 0;
spritebuff[bnbr].h = 0;
spritebuff[bnbr].next_x = 10000;
spritebuff[bnbr].next_y = 10000;
spritebuff[bnbr].layer = -1;
spritebuff[bnbr].active = false;
spritebuff[bnbr].edges = 0;
}
else error((char *)"Buffer not in use");
if (sprites_in_use != LIFOpointer + zeroLIFOpointer || sprites_in_use != sumlayer())error((char *)"sprite internal error");
} else if ((p = checkstring(cmdline, (unsigned char*)"NEXT"))) {
getargs(&p, 5, (unsigned char*)",");
if (!(argc == 5)) error((char *)"Syntax");
if (*argv[0] == '#') argv[0]++;
bnbr = (int)getint(argv[0], 1, MAXBLITBUF); // get the number
spritebuff[bnbr].next_x = (short)getint(argv[2], -spritebuff[bnbr].w + 1, maxW - 1);
spritebuff[bnbr].next_y = (short)getint(argv[4], -spritebuff[bnbr].h + 1, maxH - 1);
//
} else if ((p = checkstring(cmdline, (unsigned char*)"WRITE"))) {
int mode = 4;
getargs(&p, 7, (unsigned char*)",");
if (!(argc == 5 || argc == 7)) error((char *)"Syntax");
if (*argv[0] == '#') argv[0]++;
bnbr = (int)getint(argv[0], 1, MAXBLITBUF); // get the number
if(spritebuff[bnbr].h==9999)error("Invalid buffer");
if (spritebuff[bnbr].spritebuffptr != NULL) {
x1 = (int)getint(argv[2], -spritebuff[bnbr].w + 1, maxW);
y1 = (int)getint(argv[4], -spritebuff[bnbr].h + 1, maxH);
if (argc == 7)spritebuff[bnbr].rotation = (char)getint(argv[6], 0, 7);
else spritebuff[bnbr].rotation = 4;
if ((spritebuff[bnbr].rotation & 4) == 0)mode |= 8;
spritebuff[bnbr].rotation &= 3;
w = spritebuff[bnbr].w;
h = spritebuff[bnbr].h;
// int cursorhidden = 0;
BlitShowBuffer(bnbr, x1, y1, mode);
}
else error((char *)"Buffer not in use");
}
#ifdef rp2350
else if((p = checkstring(cmdline, (unsigned char *)"LOADPNG"))) {
int toggle=0, transparent=0, cutoff=30;
int w,h;
upng_t* upng;
// get the command line arguments
getargs(&p, 11, (unsigned char *)","); // this MUST be the first executable line in the function
if(*argv[0] == '#') argv[0]++; // check if the first arg is prefixed with a #
bnbr = getint(argv[0], 1, MAXBLITBUF); // get the buffer number
if(spritebuff[bnbr].spritebuffptr)error("Buffer % in use",bnbr);
if(argc == 0) error("Argument count");
if(!InitSDCard()) return;
unsigned char *q = getFstring(argv[2]); // get the file name
if(argc >= 5 && *argv[4])transparent=getint(argv[4],0,15);
transparent=RGB121map[transparent];
if(argc==7)cutoff=getint(argv[6],1,254);
if(strchr((char *)q, '.') == NULL) strcat((char *)q, ".png");
upng = upng_new_from_file((char *)q);
routinechecks();
upng_header(upng);
w=upng_get_width(upng);
h= upng_get_height(upng);
spritebuff[bnbr].spritebuffptr = GetMemory((w * h + 4 )>>1);
spritebuff[bnbr].blitstoreptr = GetMemory((w * h + 4 )>>1);
spritebuff[bnbr].w=w;
spritebuff[bnbr].h=h;
spritebuff[bnbr].master = 0;
spritebuff[bnbr].mymaster = -1;
spritebuff[bnbr].x = 10000;
spritebuff[bnbr].y = 10000;
spritebuff[bnbr].layer = -1;
spritebuff[bnbr].next_x = 10000;
spritebuff[bnbr].next_y = 10000;
spritebuff[bnbr].active = false;
spritebuff[bnbr].lastcollisions = 0;
spritebuff[bnbr].edges = 0;
unsigned char *t=(unsigned char *)spritebuff[bnbr].spritebuffptr;
if(w>HRes|| h >VRes){
upng_free(upng);
error("Image too large");
}
if(!(upng_get_format(upng)==3)){
upng_free(upng);
error("Invalid format, must be RGBA8888");
}
routinechecks();
upng_decode(upng);
unsigned char *rr;
routinechecks();
rr=(unsigned char *)upng_get_buffer(upng);
unsigned char *pp=rr;
char d[3];
int i=w*h;
while(i--){
d[0]=rr[2];
d[1]=rr[1];
d[2]=rr[0];
if(rr[3]>cutoff){
pp[0]=d[0];
pp[1]=d[1];
pp[2]=d[2];
} else {
pp[0]=(transparent & 0xFF0000)>>16;
pp[1]=(transparent & 0xFF00)>>8;
pp[2]=(transparent & 0xFF);
}
if(DISPLAY_TYPE==SCREENMODE1) {
if(toggle){
*t |= (char)(((uint16_t)pp[2]+(uint16_t)pp[1]+(uint16_t)pp[0])<0x180? 0 : 0xF0);
} else {
*t= (char)(((uint16_t)pp[2]+(uint16_t)pp[1]+(uint16_t)pp[0])<0x180? 0 : 0xF);
}
} else {
if(toggle){
*t |= ((pp[2] & 0x80)) | ((pp[1] & 0xC0)>>1) | ((pp[0] & 0x80)>>3);
} else {
*t = ((pp[2] & 0x80)>> 4) | ((pp[1] & 0xC0)>>5) | ((pp[0] & 0x80)>>7);
}
}
if(toggle) t++;
toggle=!toggle;
pp+=3;
rr+=4;
}
upng_free(upng);
return;
}
#endif
else if((p = checkstring(cmdline, (unsigned char *)"LOADBMP"))) {
int fnbr,toggle=0;
int xOrigin, yOrigin, xlen, ylen;
BMPDECODER BmpDec;
// get the command line arguments
getargs(&p, 11, (unsigned char *)","); // this MUST be the first executable line in the function
if(*argv[0] == '#') argv[0]++; // check if the first arg is prefixed with a #
bnbr = getint(argv[0], 1, MAXBLITBUF); // get the buffer number
if(spritebuff[bnbr].spritebuffptr)error("Buffer % in use",bnbr);
if(argc == 0) error("Argument count");
if(!InitSDCard()) return;
unsigned char *pp = getFstring(argv[2]); // get the file name
xOrigin = yOrigin = 0;
if(argc >= 5 && *argv[4]) xOrigin = getinteger(argv[4]); // get the x origin (optional) argument
if(argc >= 7 && *argv[6]) yOrigin = getinteger(argv[6]); // get the y origin (optional) argument
if(xOrigin<0 || yOrigin<0)error("Coordinates");
xlen = ylen =-1;
if(argc >= 9 && *argv[8]) xlen = getinteger(argv[8]); // get the x length (optional) argument
if(argc == 11 ) ylen = getinteger(argv[10]); // get the y length (optional) argument
// open the file
if(strchr((char *)pp, '.') == NULL) strcat((char *)pp, ".bmp");
fnbr = FindFreeFileNbr();
if(!BasicFileOpen((char *)pp, fnbr, FA_READ)) return;
BDEC_bReadHeader(&BmpDec, fnbr);
FileClose(fnbr);
if(xlen==-1)xlen=BmpDec.lWidth;
if(ylen==-1)ylen=BmpDec.lHeight;
if(xlen+xOrigin>BmpDec.lWidth || ylen+yOrigin>BmpDec.lHeight)error("Coordinates");
char *q=GetTempMemory(xlen * ylen * 3);
spritebuff[bnbr].spritebuffptr = GetMemory((xlen * ylen +4 )>>1);
spritebuff[bnbr].blitstoreptr = GetMemory((xlen * ylen +4 )>>1);
memset(q,0xFF,xlen * ylen * 3);
fnbr = FindFreeFileNbr();
if(!BasicFileOpen((char *)pp, fnbr, FA_READ)) return;
BMP_bDecode_memory(xOrigin, yOrigin, xlen, ylen, fnbr, q);
spritebuff[bnbr].w=xlen;
spritebuff[bnbr].h=ylen;
spritebuff[bnbr].master = 0;
spritebuff[bnbr].mymaster = -1;
spritebuff[bnbr].x = 10000;
spritebuff[bnbr].y = 10000;
spritebuff[bnbr].layer = -1;
spritebuff[bnbr].next_x = 10000;
spritebuff[bnbr].next_y = 10000;
spritebuff[bnbr].active = false;
spritebuff[bnbr].lastcollisions = 0;
spritebuff[bnbr].edges = 0;
char *t=spritebuff[bnbr].spritebuffptr;
int i=xlen*ylen;
while(i--){
if(DISPLAY_TYPE==SCREENMODE1) {
if(toggle){
*t |= (char)(((uint16_t)q[2]+(uint16_t)q[1]+(uint16_t)q[0])<0x180? 0 : 0xF0);
} else {
*t= (char)(((uint16_t)q[2]+(uint16_t)q[1]+(uint16_t)q[0])<0x180? 0 : 0xF);
}
} else {
if(toggle){
*t |= ((q[2] & 0x80)) | ((q[1] & 0xC0)>>1) | ((q[0] & 0x80)>>3);
} else {
*t = ((q[2] & 0x80)>> 4) | ((q[1] & 0xC0)>>5) | ((q[0] & 0x80)>>7);
}
}
if(toggle) t++;
toggle=!toggle;
q+=3;
}
FileClose(fnbr);
return;
} else if ((p = checkstring(cmdline, (unsigned char*)"MOVE"))) {
if (hideall)error((char *)"Sprites are hidden");
int i;
// int cursorhidden = 0;
for (i = LIFOpointer - 1; i >= 0; i--) blithide(LIFO[i], 0);
for (i = zeroLIFOpointer - 1; i >= 0; i--)blithide(zeroLIFO[i], 0);
//
for (i = 0; i < zeroLIFOpointer; i++) {
if (spritebuff[zeroLIFO[i]].next_x != 10000) {
spritebuff[zeroLIFO[i]].x = spritebuff[zeroLIFO[i]].next_x;
spritebuff[zeroLIFO[i]].next_x = 10000;
}
if (spritebuff[zeroLIFO[i]].next_y != 10000) {
spritebuff[zeroLIFO[i]].y = spritebuff[zeroLIFO[i]].next_y;
spritebuff[zeroLIFO[i]].next_y = 10000;
}
BlitShowBuffer(zeroLIFO[i], spritebuff[zeroLIFO[i]].x, spritebuff[zeroLIFO[i]].y, 0);
}
for (i = 0; i < LIFOpointer; i++) {
if (spritebuff[LIFO[i]].next_x != 10000) {
spritebuff[LIFO[i]].x = spritebuff[LIFO[i]].next_x;
spritebuff[LIFO[i]].next_x = 10000;
}
if (spritebuff[LIFO[i]].next_y != 10000) {
spritebuff[LIFO[i]].y = spritebuff[LIFO[i]].next_y;
spritebuff[LIFO[i]].next_y = 10000;
}
BlitShowBuffer(LIFO[i], spritebuff[LIFO[i]].x, spritebuff[LIFO[i]].y, 0);
}
ProcessCollisions(0);
} else if ((p = checkstring(cmdline, (unsigned char*)"SCROLL"))) {
int i, n, m = 0, blank = -2, x, y;
char* current = NULL;
getargs(&p, 5, (unsigned char*)",");
if (hideall)error((char *)"Sprites are hidden");
x = (int)getint(argv[0], -maxW / 2 - 1, maxW);
y = (int)getint(argv[2], -maxH / 2 - 1, maxH);
if (argc == 5)blank = (int)getColour((char *)argv[2], 1);
if (!(x == 0 && y == 0)) {
m = ((maxW * (y > 0 ? y : -y)+1) >>1);
n = ((maxH * (x > 0 ? x : -x)+1) >>1);
if (n > m)m = n;
if (blank == -2)current = (char *)GetMemory(m);
for (i = LIFOpointer - 1; i >= 0; i--) blithide(LIFO[i], 0);
for (i = zeroLIFOpointer - 1; i >= 0; i--) {
int xs = spritebuff[zeroLIFO[i]].x + (spritebuff[zeroLIFO[i]].w >> 1);
int ys = spritebuff[zeroLIFO[i]].y + (spritebuff[zeroLIFO[i]].h >> 1);
blithide(zeroLIFO[i], 0);
xs += x;
if (xs >= maxW)xs -= maxW;
if (xs < 0)xs += maxW;
spritebuff[zeroLIFO[i]].x = xs - (spritebuff[zeroLIFO[i]].w >> 1);
ys -= y;
if (ys >= maxH)ys -= maxH;
if (ys < 0)ys += maxH;
spritebuff[zeroLIFO[i]].y = ys - (spritebuff[zeroLIFO[i]].h >> 1);
}
if (x > 0) {
if (blank == -2)ReadBufferFast(maxW - x, 0, maxW - 1, maxH - 1, (unsigned char *)current);
ScrollBufferH(x);
if (blank == -2)DrawBufferFast(0, 0, x - 1, maxH - 1, -1, (unsigned char *)current);
else if (blank != -1)DrawRectangle(0, 0, x - 1, maxH - 1, blank);
}
else if (x < 0) {
x = -x;
if (blank == -2)ReadBufferFast(0, 0, x - 1, maxH - 1, (unsigned char *)current);
ScrollBufferH(-x);
if (blank == -2)DrawBufferFast(maxW - x, 0, maxW - 1, maxH - 1, -1, (unsigned char *)current);
else if (blank != -1)DrawRectangle(maxW - x, 0, maxW - 1, maxH - 1, blank);
}
if (y > 0) {
if (blank == -2)ReadBufferFast(0, 0, maxW - 1, y - 1, (unsigned char *)current);
ScrollBufferV(y, 0);
if (blank == -2)DrawBufferFast(0, maxH - y, maxW - 1, maxH - 1, -1, (unsigned char *)current);
else if (blank != -1)DrawRectangle(0, maxH - y, maxW - 1, maxH - 1, blank);
}
else if (y < 0) {
y = -y;
if (blank == -2)ReadBufferFast(0, maxH - y, maxW - 1, maxH - 1, (unsigned char *)current);
ScrollBufferV(-y, 0);
if (blank == -2)DrawBufferFast(0, 0, maxW - 1, y - 1, -1, (unsigned char *)current);
else if (blank != -1)DrawRectangle(0, 0, maxW - 1, y - 1, blank);
}
for (i = 0; i < zeroLIFOpointer; i++) {
BlitShowBuffer(zeroLIFO[i], spritebuff[zeroLIFO[i]].x, spritebuff[zeroLIFO[i]].y, 0);
}
for (i = 0; i < LIFOpointer; i++) {
if (spritebuff[LIFO[i]].next_x != 10000) {
spritebuff[LIFO[i]].x = spritebuff[LIFO[i]].next_x;
spritebuff[LIFO[i]].next_x = 10000;
}
if (spritebuff[LIFO[i]].next_y != 10000) {
spritebuff[LIFO[i]].y = spritebuff[LIFO[i]].next_y;
spritebuff[LIFO[i]].next_y = 10000;
}
BlitShowBuffer(LIFO[i], spritebuff[LIFO[i]].x, spritebuff[LIFO[i]].y, 0);
}
ProcessCollisions(0);
if (current)FreeMemory((unsigned char *)current);
}
} else if ((p = checkstring(cmdline, (unsigned char*)"SET TRANSPARENT"))) {
sprite_transparent=getint((unsigned char *)p,0,15);
} else error("Syntax");
}
void fun_sprite(void) {
int bnbr = 0, w = -1, h = -1, t = 0, x = 10000, y = 10000, l = 0, n, c = 0;
getargs(&ep, 5, (unsigned char *)",");
if (checkstring(argv[0], (unsigned char*)"W")) t = 1;
else if (checkstring(argv[0], (unsigned char*)"H")) t = 2;
else if (checkstring(argv[0], (unsigned char*)"X")) t = 3;
else if (checkstring(argv[0], (unsigned char*)"Y")) t = 4;
else if (checkstring(argv[0], (unsigned char*)"L")) t = 5;
else if (checkstring(argv[0], (unsigned char*)"C")) t = 6;
else if (checkstring(argv[0], (unsigned char*)"V")) t = 7;
else if (checkstring(argv[0], (unsigned char*)"T")) t = 8;
else if (checkstring(argv[0], (unsigned char*)"E")) t = 9;
else if (checkstring(argv[0], (unsigned char*)"D")) t = 10;
else if (checkstring(argv[0], (unsigned char*)"A")) t = 11;
else if (checkstring(argv[0], (unsigned char*)"N")) t = 12;
else if (checkstring(argv[0], (unsigned char*)"S")) t = 13;
else error((char *)"Syntax");
if (t < 12) {
if (argc < 3)error((char *)"Syntax");
if (*argv[2] == '#') argv[2]++;
bnbr = (int)getint(argv[2], 0, MAXBLITBUF);
if (bnbr == 0) {
if (argc == 5 && !(t == 7 || t == 10)) {
n = (int)getint(argv[4], 1, spritebuff[0].collisions[0]);
c = spritebuff[0].collisions[n];
}
else c = spritebuff[0].collisions[0];
}
if (spritebuff[bnbr].spritebuffptr != NULL) {
w = spritebuff[bnbr].w;
h = spritebuff[bnbr].h;
}
if (spritebuff[bnbr].active) {
x = spritebuff[bnbr].x;
y = spritebuff[bnbr].y;
l = spritebuff[bnbr].layer;
if (argc == 5 && !(t == 7 || t == 10)) {
n = (int)getint(argv[4], 1, spritebuff[bnbr].collisions[0]);
c = spritebuff[bnbr].collisions[n];
}
else c = spritebuff[bnbr].collisions[0];
}
}
if (t == 1)iret = w;
else if (t == 2)iret = h;
else if (t == 3) { if (spritebuff[bnbr].active)iret = x; else iret = 10000; }
else if (t == 4) { if (spritebuff[bnbr].active)iret = y; else iret = 10000; }
else if (t == 5) { if (spritebuff[bnbr].active)iret = l; else iret = -1; }
else if (t == 8) { if (spritebuff[bnbr].active)iret = spritebuff[bnbr].lastcollisions; else iret = 0; }
else if (t == 9) { if (spritebuff[bnbr].active)iret = spritebuff[bnbr].edges; else iret = 0; }
else if (t == 6) { if (spritebuff[bnbr].collisions[0])iret = c; else iret = -1; }
else if (t == 11) iret = (int64_t)((uint32_t)spritebuff[bnbr].spritebuffptr);
else if (t == 7) {
int rbnbr = 0;
int x1 = 0, y1 = 0, h1 = 0, w1 = 0;
MMFLOAT vector;
if (argc < 5)error((char *)"Syntax");
if (*argv[4] == '#') argv[4]++;
rbnbr = (int)getint(argv[4], 1, MAXBLITBUF);
if (spritebuff[rbnbr].spritebuffptr != NULL) {
w1 = spritebuff[rbnbr].w;
h1 = spritebuff[rbnbr].h;
}
if (spritebuff[rbnbr].active) {
x1 = spritebuff[rbnbr].x;
y1 = spritebuff[rbnbr].y;
}
if (!(spritebuff[bnbr].active && spritebuff[rbnbr].active))fret = -1.0;
else {
x += w / 2;
y += h / 2;
x1 += w1 / 2;
y1 += h1 / 2;
y1 -= y;
x1 -= x;
vector = atan2(y1, x1);
vector += M_PI_2;
if (vector < 0)vector += M_TWOPI;
fret = vector;
}
targ = T_NBR;
return;
}
else if (t == 10) {
int rbnbr = 0;
int x1 = 0, y1 = 0, h1 = 0, w1 = 0;
if (argc < 5)error((char *)"Syntax");
if (*argv[4] == '#') argv[4]++;
rbnbr = (int)getint(argv[4], 1, MAXBLITBUF);
if (spritebuff[rbnbr].spritebuffptr != NULL) {
w1 = spritebuff[rbnbr].w;
h1 = spritebuff[rbnbr].h;
}
if (spritebuff[rbnbr].active) {
x1 = spritebuff[rbnbr].x;
y1 = spritebuff[rbnbr].y;
}
if (!(spritebuff[bnbr].active && spritebuff[rbnbr].active))fret = -1.0;
else {
x += w / 2;
y += h / 2;
x1 += w1 / 2;
y1 += h1 / 2;
fret = sqrt((x1 - x) * (x1 - x) + (y1 - y) * (y1 - y));
}
targ = T_NBR;
return;
}
else if (t == 12) {
if (argc == 3) {
n = (int)getint(argv[2], 0, MAXLAYER);
iret = layer_in_use[n];
}
else iret = sprites_in_use;
}
else if (t == 13) iret = sprite_which_collided;
else {
}
targ = T_INT;
}
/*
* @cond
* The following section will be excluded from the documentation.
*/
#ifndef PICOMITEVGA
void restorepanel(void){
if(Option.DISPLAY_TYPE>I2C_PANEL && Option.DISPLAY_TYPE < BufferedPanel){
if(Option.DISPLAY_ORIENTATION==PORTRAIT){
DrawRectangle = DrawRectangleSPISCR;
DrawBitmap = DrawBitmapSPISCR;
DrawBuffer = DrawBufferSPISCR;
DrawPixel = DrawPixelNormal;
DrawBLITBuffer = DrawBufferSPISCR;
ScrollLCD = ScrollLCDSPISCR;
if(Option.DISPLAY_TYPE == ILI9341 || Option.DISPLAY_TYPE == ST7796SP || Option.DISPLAY_TYPE == ST7796S || Option.DISPLAY_TYPE == ILI9488 || Option.DISPLAY_TYPE == ILI9488P || Option.DISPLAY_TYPE == ST7789B){
ReadBuffer = ReadBufferSPISCR;
ReadBLITBuffer = ReadBufferSPISCR;
}
} else {
DrawRectangle = DrawRectangleSPI;
DrawBitmap = DrawBitmapSPI;
DrawBuffer = DrawBufferSPI;
DrawPixel = DrawPixelNormal;
DrawBLITBuffer = DrawBufferSPI;
if(Option.DISPLAY_TYPE == ILI9341 || Option.DISPLAY_TYPE == ST7796SP || Option.DISPLAY_TYPE == ST7796S || Option.DISPLAY_TYPE == ILI9488 || Option.DISPLAY_TYPE == ILI9488P || Option.DISPLAY_TYPE == ST7789B){
ReadBLITBuffer = ReadBufferSPI;
ReadBuffer = ReadBufferSPI;
ScrollLCD = ScrollLCDSPI;
}
}
} else if(Option.DISPLAY_TYPE>=SSDPANEL && Option.DISPLAY_TYPE < VIRTUAL){
if(screen320){
DrawRectangle = DrawRectangle320;
DrawBitmap = DrawBitmap320;
DrawBuffer = DrawBuffer320;
ReadBuffer = ReadBuffer320;
} else {
DrawRectangle= DrawRectangleSSD1963;
DrawBitmap = DrawBitmapSSD1963;
DrawBuffer = DrawBufferSSD1963;
ReadBuffer = ReadBufferSSD1963;
if(SSD16TYPE || Option.DISPLAY_TYPE==IPS_4_16){
DrawBLITBuffer= DrawBLITBufferSSD1963;
ReadBLITBuffer = ReadBLITBufferSSD1963;
} else {
DrawBLITBuffer= DrawBufferSSD1963;
ReadBLITBuffer = ReadBufferSSD1963;
}
}
DrawPixel = DrawPixelNormal;
if(!(Option.DISPLAY_TYPE == ILI9341_8 || Option.DISPLAY_TYPE == ILI9341_16 || Option.DISPLAY_TYPE == IPS_4_16 ))ScrollLCD = ScrollSSD1963;
else ScrollLCD=ScrollLCDSPI;
}
WriteBuf=NULL;
}
void setframebuffer(void){
if(!((Option.DISPLAY_TYPE>I2C_PANEL && Option.DISPLAY_TYPE < BufferedPanel) || (Option.DISPLAY_TYPE>=SSDPANEL && Option.DISPLAY_TYPE<VIRTUAL) || Option.DISPLAY_TYPE>=NEXTGEN))return;
DrawRectangle=DrawRectangle16;
DrawBitmap= DrawBitmap16;
ScrollLCD=ScrollLCD16;
DrawBuffer=DrawBuffer16;
ReadBLITBuffer=ReadBuffer16;
DrawBLITBuffer=DrawBuffer16;
ReadBuffer=ReadBuffer16;
DrawBufferFast=DrawBuffer16Fast;
ReadBufferFast=ReadBuffer16Fast;
DrawPixel=DrawPixel16;
}
void closeframebuffer(char layer){
#ifdef PICOMITE
if(mergerunning){
multicore_fifo_push_blocking(0xFF);
busy_wait_ms(mergetimer+200);
if(mergerunning){
_excep_code = RESET_COMMAND;
SoftReset();
}
}
#endif
if(FrameBuf)FreeMemory(FrameBuf);
if(LayerBuf)FreeMemory(LayerBuf);
FrameBuf=NULL;
WriteBuf=NULL;
restorepanel();
}
void copyframetoscreen(uint8_t *s,int xstart, int xend, int ystart, int yend, int odd){
unsigned char col[3]={0};
int c;
if(Option.DISPLAY_TYPE>I2C_PANEL && Option.DISPLAY_TYPE<BufferedPanel)DefineRegionSPI(xstart,ystart,xend,yend, 1);
else if(Option.DISPLAY_TYPE == ILI9341_8){
SetAreaILI9341(xstart,ystart,xend,yend, 1);
} else if(Option.DISPLAY_TYPE == ILI9341_16 || Option.DISPLAY_TYPE == ILI9486_16) {
if(Option.DISPLAY_TYPE == ILI9486_16){
Write16bitCommand(ILI9341_PIXELFORMAT);
WriteData16bit(0x55);
}
SetAreaILI9341(xstart,ystart,xend,yend, 1);
} else if(Option.DISPLAY_TYPE==IPS_4_16) {
if(LCDAttrib==1)WriteCmdDataIPS_4_16(0x3A00,1,0x55);
if(screen320){
SetAreaIPS_4_16(xstart+80,ystart*2,xend*2-xstart+81,yend*2+1,1); // setup the area to be filled
} else {
SetAreaIPS_4_16(xstart,ystart,xend,yend, 1); // setup the area to be filled
}
} else {
if(screen320){
if(Option.DISPLAY_TYPE!=SSD1963_4_16)SetAreaSSD1963(xstart+80,ystart*2,xend*2-xstart+81,yend*2+1); // setup the area to be filled
else SetAreaSSD1963(xstart+80,ystart+16,xend+80,yend+16);
} else {
SetAreaSSD1963(xstart,ystart,xend,yend); // setup the area to be filled
}
WriteComand(CMD_WR_MEMSTART);
}
int i;
int cnt=2;
if(Option.DISPLAY_TYPE==ILI9488 || Option.DISPLAY_TYPE == ILI9488P || Option.DISPLAY_TYPE==ILI9481IPS || (Option.DISPLAY_TYPE>=SSDPANEL && Option.DISPLAY_TYPE<=SSD_PANEL_8)){
cnt=3;
}
if(map[15]==0){
for(i=0;i<16;i++){
if(Option.DISPLAY_TYPE==ILI9488 || Option.DISPLAY_TYPE == ILI9488P || Option.DISPLAY_TYPE==ILI9481IPS){
col[0]=(RGB121map[i]>>16);
col[1]=(RGB121map[i]>>8) & 0xFF;
col[2]=(RGB121map[i] & 0xFF);
} else if(Option.DISPLAY_TYPE>=SSDPANEL && Option.DISPLAY_TYPE<=SSD_PANEL_8){
col[2]=(RGB121map[i]>>16);
col[1]=(RGB121map[i]>>8) & 0xFF;
col[0]=(RGB121map[i] & 0xFF);
} else if(Option.DISPLAY_TYPE>SSD_PANEL_8){
map[i]=((RGB121map[i]>>8) & 0xf800) | ((RGB121map[i]>>5) & 0x07e0) | ((RGB121map[i]>>3) & 0x001f);
continue;
} else {
col[0]= ((RGB121map[i] >> 16) & 0b11111000) | ((RGB121map[i] >> 13) & 0b00000111);
col[1] = ((RGB121map[i] >> 5) & 0b11100000) | ((RGB121map[i] >> 3) & 0b00011111);
}
if(Option.DISPLAY_TYPE == GC9A01){
col[0]=~col[0];
col[1]=~col[1];
}
map[i]=col[0]|(col[1]<<8)|(col[2]<<16);
}
}
i=(xend-xstart+1)*(yend-ystart+1);
if(Option.DISPLAY_TYPE>I2C_PANEL && Option.DISPLAY_TYPE<BufferedPanel ){
if(PinDef[Option.SYSTEM_CLK].mode & SPI0SCK){
if(odd){
c=map[(*s & 0xF0)>>4];
spi_write_fast(spi0,(uint8_t *)&c,cnt);
s++;
i--;
}
while(i>0){
c=map[*s & 0xF];
spi_write_fast(spi0,(uint8_t *)&c,cnt);
if(i>1){
c=map[(*s & 0xF0)>>4];
spi_write_fast(spi0,(uint8_t *)&c,cnt);
}
s++;
i-=2;
}
} else {
if(odd){
c=map[(*s & 0xF0)>>4];
spi_write_fast(spi1,(uint8_t *)&c,cnt);
s++;
i--;
}
while(i>0){
c=map[*s & 0xF];
spi_write_fast(spi1,(uint8_t *)&c,cnt);
if(i>1){
c=map[(*s & 0xF0)>>4];
spi_write_fast(spi1,(uint8_t *)&c,cnt);
}
s++;
i-=2;
}
}
if(PinDef[Option.SYSTEM_CLK].mode & SPI0SCK)spi_finish(spi0);
else spi_finish(spi1);
ClearCS(Option.LCD_CS); //set CS high
} else {
if(screen320 && Option.DISPLAY_TYPE!=SSD1963_4_16){
unsigned char *q = buff320;
HRes=720;
VRes=480;
uint16_t *pp=(uint16_t *)q;
if(odd){ //only used for a single line
if(odd){
c=map[(*s & 0xF0)>>4];
*pp++=c;
gpio_put(SSD1963_WR_GPPIN,0);
gpio_put_masked64(0xFFFF<<SSD1963data,c<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,1);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;gpio_put(SSD1963_WR_GPPIN,1);
s++;
i--;
int x=1;
while(x<=xend-xstart){
c=map[*s & 0xF];
*pp++=c;
gpio_put(SSD1963_WR_GPPIN,0);
gpio_put_masked64(0xFFFF<<SSD1963data,c<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,1);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;gpio_put(SSD1963_WR_GPPIN,1);
if(i>1){
c=map[(*s & 0xF0)>>4];
*pp++=c;
gpio_put(SSD1963_WR_GPPIN,0);
gpio_put_masked64(0xFFFF<<SSD1963data,c<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,1);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;gpio_put(SSD1963_WR_GPPIN,1);
}
s++;
i-=2;
x+=2;
}
pp=(uint16_t *)q;
for(int x=xstart;x<=xend;x++){
gpio_put(SSD1963_WR_GPPIN,0);
gpio_put_masked64(0xFFFF<<SSD1963data,(*pp++)<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,1);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;gpio_put(SSD1963_WR_GPPIN,1);
}
}
} else {
for(int y=ystart;y<=yend;y++){
pp=(uint16_t *)q;
int x=0;
while(x<=xend-xstart){
c=map[*s & 0xF];
*pp++=c;
gpio_put(SSD1963_WR_GPPIN,0);
gpio_put_masked64(0xFFFF<<SSD1963data,c<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,1);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;gpio_put(SSD1963_WR_GPPIN,1);
if(i>1){
c=map[(*s & 0xF0)>>4];
*pp++=c;
gpio_put(SSD1963_WR_GPPIN,0);
gpio_put_masked64(0xFFFF<<SSD1963data,c<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,1);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;gpio_put(SSD1963_WR_GPPIN,1);
}
s++;
i-=2;
x+=2;
}
pp=(uint16_t *)q;
for(int x=xstart;x<=xend;x++){
gpio_put(SSD1963_WR_GPPIN,0);
gpio_put_masked64(0xFFFF<<SSD1963data,(*pp++)<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,1);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;gpio_put(SSD1963_WR_GPPIN,1);
}
}
}
HRes=320;
VRes=240;
} else {
if(Option.DISPLAY_TYPE>SSD_PANEL_8){
if(odd){
c=map[(*s & 0xF0)>>4];
gpio_put_masked64(0xFFFF<<SSD1963data,c<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;nop;gpio_put(SSD1963_WR_GPPIN,1);
s++;
i--;
}
while(i>0){
c=map[*s & 0xF];
gpio_put_masked64(0xFFFF<<SSD1963data,c<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;nop;gpio_put(SSD1963_WR_GPPIN,1);
if(i>1){
c=map[(*s & 0xF0)>>4];
gpio_put_masked64(0xFFFF<<SSD1963data,c<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;nop;gpio_put(SSD1963_WR_GPPIN,1);
}
s++;
i-=2;
}
} else {
if(odd){
c=map[(*s & 0xF0)>>4];
gpio_put_masked64(0b11111111<<SSD1963data,((c >> 16) & 0xff)<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;nop;gpio_put(SSD1963_WR_GPPIN,1);
gpio_put_masked64(0b11111111<<SSD1963data,((c >> 8) & 0xff)<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;nop;gpio_put(SSD1963_WR_GPPIN,1);
nop;gpio_put_masked64(0b11111111<<SSD1963data,(c & 0xff)<<SSD1963data);
gpio_put(SSD1963_WR_GPPIN,0);nop;nop;gpio_put(SSD1963_WR_GPPIN,1);
s++;
i--;
}
while(i>0){
c=map[*s & 0xF];
gpio_put_masked64(0b11111111<<SSD1963data,((c >> 16) & 0xff)<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;nop;gpio_put(SSD1963_WR_GPPIN,1);
gpio_put_masked64(0b11111111<<SSD1963data,((c >> 8) & 0xff)<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;nop;gpio_put(SSD1963_WR_GPPIN,1);
nop;gpio_put_masked64(0b11111111<<SSD1963data,(c & 0xff)<<SSD1963data);
gpio_put(SSD1963_WR_GPPIN,0);nop;nop;gpio_put(SSD1963_WR_GPPIN,1);
if(i>1){
c=map[(*s & 0xF0)>>4];
gpio_put_masked64(0b11111111<<SSD1963data,((c >> 16) & 0xff)<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;nop;gpio_put(SSD1963_WR_GPPIN,1);
gpio_put_masked64(0b11111111<<SSD1963data,((c >> 8) & 0xff)<<SSD1963data);
nop;gpio_put(SSD1963_WR_GPPIN,0);nop;nop;gpio_put(SSD1963_WR_GPPIN,1);
nop;gpio_put_masked64(0b11111111<<SSD1963data,(c & 0xff)<<SSD1963data);
gpio_put(SSD1963_WR_GPPIN,0);nop;nop;gpio_put(SSD1963_WR_GPPIN,1);
}
s++;
i-=2;
}
}
}
}
}
void blitmerge (int x0, int y0, int w, int h, uint8_t colour){
if(LayerBuf==NULL || FrameBuf==NULL)return;
uint8_t *ss,*s=LayerBuf;
uint8_t *d=FrameBuf;
uint8_t LineBuf[HRes/2];
uint8_t highcolour=colour<<4;
#ifdef PICOMITE
mutex_enter_blocking(&frameBufferMutex); // lock the frame buffer
#endif
if(Option.DISPLAY_TYPE==ILI9341 || Option.DISPLAY_TYPE == ST7796SP || Option.DISPLAY_TYPE == ST7796S || Option.DISPLAY_TYPE==ST7789B || Option.DISPLAY_TYPE==ILI9488 || Option.DISPLAY_TYPE == ILI9488P ){
while(GetLineILI9341()!=0){}
}
for(int y=y0;y<y0+h;y++){
if(y>VRes-1)break;
memcpy(LineBuf,d+y*HRes/2,HRes/2);
ss=s+y*HRes/2;
for(int x=0;x<HRes/2;x++){
uint8_t top=*ss & 0xF0;
uint8_t bottom=*ss++ &0x0f;
if(top==highcolour && bottom==colour)continue;
if(top!=highcolour && bottom!=colour)LineBuf[x]=(top|bottom);
else if(top!=highcolour){
LineBuf[x]&=0x0F;
LineBuf[x]|=top;
} else {
LineBuf[x]&=0xF0;
LineBuf[x]|=bottom;
}
}
copyframetoscreen(&LineBuf[x0/2],x0,x0+w-1,y,y,0);
}
#ifdef PICOMITE
mutex_exit(&frameBufferMutex);
mergedone=true;
__dmb();
#endif
}
void merge(uint8_t colour){
if(LayerBuf==NULL || FrameBuf==NULL)return;
uint8_t *ss,*s=LayerBuf;
uint8_t *d=FrameBuf;
uint8_t LineBuf[HRes/2];
uint8_t highcolour=colour<<4;
#ifdef PICOMITE
mutex_enter_blocking(&frameBufferMutex); // lock the frame buffer
#endif
if(Option.DISPLAY_TYPE==ILI9341 || Option.DISPLAY_TYPE == ST7796SP || Option.DISPLAY_TYPE == ST7796S || Option.DISPLAY_TYPE==ST7789B || Option.DISPLAY_TYPE==ILI9488 || Option.DISPLAY_TYPE == ILI9488P ){
while(GetLineILI9341()!=0){}
}
for(int y=0;y<VRes;y++){
memcpy(LineBuf,d+y*HRes/2,HRes/2);
ss=s+y*HRes/2;
for(int x=0;x<HRes/2;x++){
uint8_t top=*ss & 0xF0;
uint8_t bottom=*ss++ &0x0f;
if(top==highcolour && bottom==colour)continue;
if(top!=highcolour && bottom!=colour)LineBuf[x]=(top|bottom);
else if(top!=highcolour){
LineBuf[x]&=0x0F;
LineBuf[x]|=top;
} else {
LineBuf[x]&=0xF0;
LineBuf[x]|=bottom;
}
}
copyframetoscreen(LineBuf,0,HRes-1,y,y,0);
}
#ifdef PICOMITE
mutex_exit(&frameBufferMutex);
mergedone=true;
__dmb();
#endif
}
/* @endcond */
void cmd_framebuffer(void){
unsigned char *p=NULL;
if((p=checkstring(cmdline, (unsigned char *)"CREATE"))) {
if(FrameBuf==NULL){
FrameBuf=GetMemory(HRes*VRes/2);
}
else error("Framebuffer already exists");
} else if((p=checkstring(cmdline, (unsigned char *)"WRITE"))) {
if(checkstring(p, (unsigned char *)"N")){
#ifdef PICOMITE
if(mergerunning)error("Display in use for merged operation");
#endif
restorepanel();
return;
}
else if(checkstring(p, (unsigned char *)"L")){
if(!LayerBuf)error("Layer buffer not created");
WriteBuf=LayerBuf;
setframebuffer();
return;
}
else if(checkstring(p, (unsigned char *)"F")){
if(!FrameBuf)error("Frame buffer not created");
WriteBuf=FrameBuf;
setframebuffer();
return;
}
{
getargs(&p,1,(unsigned char *)",");
if(argc!=1)error("Syntax");
char *q=(char *)getCstring(argv[0]);
if(strcasecmp(q,"N")==0){
#ifdef PICOMITE
if(mergerunning)error("Display in use for merged operation");
#endif
restorepanel();
} else if(strcasecmp(q,"L")==0){
if(!LayerBuf)error("Layer buffer not created");
WriteBuf=LayerBuf;
setframebuffer();
} else if(strcasecmp(q,"F")==0){
if(!FrameBuf)error("Frame buffer not created");
WriteBuf=FrameBuf;
setframebuffer();
} else error("Syntax");
}
#ifndef PICOMITEVGA
#ifdef PICOMITE
} else if((p=checkstring(cmdline, (unsigned char *)"SYNC"))) { //merge the layer onto the physical display
mergedone=false;
while(mergedone==false){CheckAbort();}
#endif
} else if((p=checkstring(cmdline, (unsigned char *)"MERGE"))) { //merge the layer onto the physical display
if(!LayerBuf)error("Layer not created");
if(!FrameBuf)error("Framebuffer not created");
uint8_t colour=0;
getargs(&p,5,(unsigned char *)",");
if(argc>=1 && *argv[0]){
colour=getint(argv[0],0,15);
}
#ifdef PICOMITE
uint8_t background=0;
if(argc>=3 && *argv[2]){
if(checkstring(argv[2],(unsigned char *)"B"))background=1;
else if(checkstring(argv[2],(unsigned char *)"R"))background=2;
else if(checkstring(argv[2],(unsigned char *)"A"))background=3;
else error("Syntax");
}
if(background==1){
if(!(((Option.DISPLAY_TYPE>I2C_PANEL && Option.DISPLAY_TYPE<BufferedPanel ) || (Option.DISPLAY_TYPE>=SSDPANEL && Option.DISPLAY_TYPE<VIRTUAL))))error("Not available on this display");
if(diskchecktimer<200 && SPIatRisk)diskchecktimer = 200;
multicore_fifo_push_blocking(2);
multicore_fifo_push_blocking((uint32_t)colour);
} else if(background==2){
mergetimer=0;
if(argc==5)mergetimer=getint(argv[4],0,60*10*1000);
if(!(((Option.DISPLAY_TYPE>I2C_PANEL && Option.DISPLAY_TYPE<BufferedPanel ) || (Option.DISPLAY_TYPE>=SSDPANEL && Option.DISPLAY_TYPE<VIRTUAL))))error("Not available on this display");
if(WriteBuf==NULL)WriteBuf=FrameBuf;
setframebuffer();
multicore_fifo_push_blocking(3);
multicore_fifo_push_blocking((uint32_t)colour);
multicore_fifo_push_blocking((uint32_t)mergetimer*1000);
} else if(background==3){
if(mergerunning){
multicore_fifo_push_blocking(0xFF);
busy_wait_ms(mergetimer+200);
if(mergerunning){
_excep_code = RESET_COMMAND;
SoftReset();
}
}
} else
#endif
merge(colour);
#endif
} else if((p=checkstring(cmdline, (unsigned char *)"LAYER"))) {
if(LayerBuf==NULL){
LayerBuf=GetMemory(HRes*VRes/2);
} else error("Layer already exists");
} else if((p=checkstring(cmdline, (unsigned char *)"WAIT"))) {
if(Option.DISPLAY_TYPE==ILI9341 || Option.DISPLAY_TYPE == ST7796SP || Option.DISPLAY_TYPE == ST7796S || Option.DISPLAY_TYPE==ST7789B || Option.DISPLAY_TYPE==ILI9488 || Option.DISPLAY_TYPE == ILI9488P ){
while(GetLineILI9341()!=0){}
}
} else if((p=checkstring(cmdline, (unsigned char *)"CLOSE"))) {
if(checkstring(p, (unsigned char *)"F")){
#ifdef PICOMITE
if(mergerunning){
multicore_fifo_push_blocking(0xFF);
busy_wait_ms(mergetimer+200);
if(mergerunning){
_excep_code = RESET_COMMAND;
SoftReset();
}
}
#endif
if(WriteBuf!=LayerBuf)restorepanel();
if(FrameBuf)FreeMemory(FrameBuf);
FrameBuf=NULL;
} else if(checkstring(p, (unsigned char *)"L")){
#ifdef PICOMITE
if(mergerunning){
multicore_fifo_push_blocking(0xFF);
busy_wait_ms(mergetimer+200);
if(mergerunning){
_excep_code = RESET_COMMAND;
SoftReset();
}
}
#endif
if(WriteBuf!=FrameBuf)restorepanel();
if(LayerBuf)FreeMemory(LayerBuf);
LayerBuf=NULL;
} else closeframebuffer('A');
} else if((p=checkstring(cmdline, (unsigned char *)"COPY"))) {
#ifdef PICOMITE
int complex=0, background=0;
unsigned char *buff = WriteBuf;
getargs(&p,5,(unsigned char *)",");
if(!(argc==3 || argc==5))error("Syntax");
if(argc==5){
if(checkstring(argv[4],(unsigned char *)"B"))background=1;
else error("Syntax");
}
#else
int complex=0;
unsigned char *buff = WriteBuf;
getargs(&p,3,(unsigned char *)",");
if(!(argc==3))error("Syntax");
#endif
uint8_t *s=NULL,*d=NULL;
if(checkstring(argv[0],(unsigned char *)"N")){
complex=1;
if((void *)ReadBuffer == (void *)DisplayNotSet) error("Invalid on this display");
}
else if(checkstring(argv[0],(unsigned char *)"L"))s=LayerBuf;
else if(checkstring(argv[0],(unsigned char *)"F"))s=FrameBuf;
else error("Syntax");
if(checkstring(argv[2],(unsigned char *)"N")){
complex=2;
}
else if(checkstring(argv[2],(unsigned char *)"L"))d=LayerBuf;
else if(checkstring(argv[2],(unsigned char *)"F"))d=FrameBuf;
else error("Syntax");
if(d!=s){
if(!complex) memcpy(d,s,HRes*VRes/2);
else {
if(complex==1){//copying from the real display
char *LCDBuffer=GetTempMemory(HRes*3);
int DisplayMode=0;
if(DrawBufferSPI==DrawBuffer || DrawBufferSSD1963==DrawBuffer) DisplayMode=1;
WriteBuf=d;
for(int y=0;y<VRes;y++){
restorepanel();
ReadBuffer(0,y,HRes-1,y,(unsigned char *)LCDBuffer);
WriteBuf=d;
setframebuffer();
DrawBuffer(0,y,HRes-1,y,(unsigned char *)LCDBuffer);
}
if(DisplayMode) restorepanel();
} else { //copying to the real display
#ifdef PICOMITE
if(background){
if(!(((Option.DISPLAY_TYPE>I2C_PANEL && Option.DISPLAY_TYPE<BufferedPanel ) || (Option.DISPLAY_TYPE>=SSDPANEL && Option.DISPLAY_TYPE<VIRTUAL))))error("Not available on this display");
if(diskchecktimer<100 && SPIatRisk) diskchecktimer=100;
multicore_fifo_push_blocking(1);
multicore_fifo_push_blocking((uint32_t)s);
} else {
#endif
copyframetoscreen(s,0,HRes-1,0,VRes-1,0);
#ifdef PICOMITE
}
#endif
}
}
}
WriteBuf=buff;
} else error("Syntax");
}
#endif
void cmd_blit(void) {
int x1, y1, x2, y2, w, h, bnbr;
unsigned char *buff = NULL;
unsigned char *p;
if(Option.DISPLAY_TYPE == 0) error("Display not configured");
if(blitother())return;
p = checkstring(cmdline, (unsigned char *)"LOADBMP");
if(p==NULL)p = checkstring(cmdline, (unsigned char *)"LOAD");
if(p) {
int fnbr;
int xOrigin, yOrigin, xlen, ylen;
BMPDECODER BmpDec;
// get the command line arguments
getargs(&p, 11, (unsigned char *)","); // this MUST be the first executable line in the function
if(*argv[0] == '#') argv[0]++; // check if the first arg is prefixed with a #
bnbr = getint(argv[0], 1, MAXBLITBUF) - 1; // get the buffer number
if(blitbuff[bnbr].blitbuffptr)error("Buffer % in use",bnbr);
if(argc == 0) error("Argument count");
if(!InitSDCard()) return;
p = getCstring(argv[2]); // get the file name
xOrigin = yOrigin = 0;
if(argc >= 5 && *argv[4]) xOrigin = getinteger(argv[4]); // get the x origin (optional) argument
if(argc >= 7 && *argv[6]) yOrigin = getinteger(argv[6]); // get the y origin (optional) argument
if(xOrigin<0 || yOrigin<0)error("Coordinates");
xlen = ylen =-1;
if(argc >= 9 && *argv[8]) xlen = getinteger(argv[8]); // get the x length (optional) argument
if(argc == 11 ) ylen = getinteger(argv[10]); // get the y length (optional) argument
// open the file
if(strchr((char *)p, '.') == NULL) strcat((char *)p, ".bmp");
fnbr = FindFreeFileNbr();
if(!BasicFileOpen((char *)p, fnbr, FA_READ)) return;
BDEC_bReadHeader(&BmpDec, fnbr);
FileClose(fnbr);
if(xlen==-1)xlen=BmpDec.lWidth;
if(ylen==-1)ylen=BmpDec.lHeight;
if(xlen+xOrigin>BmpDec.lWidth || ylen+yOrigin>BmpDec.lHeight)error("Coordinates");
blitbuff[bnbr].blitbuffptr = GetMemory(xlen * ylen * 3 +4 );
memset(blitbuff[bnbr].blitbuffptr,0xFF,xlen * ylen * 3 +4 );
fnbr = FindFreeFileNbr();
if(!BasicFileOpen((char *)p, fnbr, FA_READ)) return;
BMP_bDecode_memory(xOrigin, yOrigin, xlen, ylen, fnbr, blitbuff[bnbr].blitbuffptr);
blitbuff[bnbr].w=xlen;
blitbuff[bnbr].h=ylen;
FileClose(fnbr);
return;
}
#ifndef PICOMITEVGA
if((p=checkstring(cmdline, (unsigned char *)"MERGE"))) { //merge the layer onto the physical display
if(!LayerBuf)error("Layer not created");
if(!FrameBuf)error("Framebuffer not created");
uint8_t colour=0;
getargs(&p,13,(unsigned char *)",");
if(argc>=1 && *argv[0]){
colour=getint(argv[0],0,15);
}
x1 = getinteger(argv[2]);
y1 = getinteger(argv[4]);
w = getinteger(argv[6]);
h = getinteger(argv[8]);
#ifdef PICOMITE
uint8_t background=0;
if(argc>=11 && *argv[10]){
if(checkstring(argv[10],(unsigned char *)"B"))background=1;
else if(checkstring(argv[10],(unsigned char *)"R"))background=2;
else if(checkstring(argv[10],(unsigned char *)"A"))background=3;
else error("Syntax");
}
if(background==1){
if(!(((Option.DISPLAY_TYPE>I2C_PANEL && Option.DISPLAY_TYPE<BufferedPanel ) || (Option.DISPLAY_TYPE>=SSDPANEL && Option.DISPLAY_TYPE<VIRTUAL))))error("Not available on this display");
if(diskchecktimer<200 && SPIatRisk)diskchecktimer = 200;
multicore_fifo_push_blocking(4);
multicore_fifo_push_blocking(x1);
multicore_fifo_push_blocking(y1);
multicore_fifo_push_blocking(w);
multicore_fifo_push_blocking(h);
multicore_fifo_push_blocking((uint32_t)colour);
} else if(background==2){
mergetimer=0;
if(argc==13)mergetimer=getint(argv[12],0,60*10*1000);
if(!(((Option.DISPLAY_TYPE>I2C_PANEL && Option.DISPLAY_TYPE<BufferedPanel ) || (Option.DISPLAY_TYPE>=SSDPANEL && Option.DISPLAY_TYPE<VIRTUAL))))error("Not available on this display");
if(WriteBuf==NULL)WriteBuf=FrameBuf;
setframebuffer();
multicore_fifo_push_blocking(5);
multicore_fifo_push_blocking(x1);
multicore_fifo_push_blocking(y1);
multicore_fifo_push_blocking(w);
multicore_fifo_push_blocking(h);
multicore_fifo_push_blocking((uint32_t)colour);
multicore_fifo_push_blocking((uint32_t)mergetimer*1000);
} else if(background==3){
if(mergerunning){
multicore_fifo_push_blocking(0xFF);
busy_wait_ms(mergetimer+200);
if(mergerunning){
_excep_code = RESET_COMMAND;
SoftReset();
}
}
} else
#endif
blitmerge(x1,y1,w,h,colour);
return;
}
#endif
if((p = checkstring(cmdline, (unsigned char *)"READ"))) {
getargs(&p, 9, (unsigned char *)",");
if((void *)ReadBuffer == (void *)DisplayNotSet) error("Invalid on this display");
if(argc !=9) error("Syntax");
if(*argv[0] == '#') argv[0]++; // check if the first arg is prefixed with a #
bnbr = getint(argv[0], 1, MAXBLITBUF) - 1; // get the buffer number
x1 = getinteger(argv[2]);
y1 = getinteger(argv[4]);
w = getinteger(argv[6]);
h = getinteger(argv[8]);
if(w < 1 || h < 1) return;
if(x1 < 0) { x2 -= x1; w += x1; x1 = 0; }
if(y1 < 0) { y2 -= y1; h += y1; y1 = 0; }
if(x1 + w > HRes) w = HRes - x1;
if(y1 + h > VRes) h = VRes - y1;
if(w < 1 || h < 1 || x1 < 0 || x1 + w > HRes || y1 < 0 || y1 + h > VRes ) return;
if(blitbuff[bnbr].blitbuffptr == NULL){
blitbuff[bnbr].blitbuffptr = GetMemory(w * h * 3);
ReadBuffer(x1, y1, x1 + w - 1, y1 + h - 1, (unsigned char *)blitbuff[bnbr].blitbuffptr);
blitbuff[bnbr].w=w;
blitbuff[bnbr].h=h;
} else error("Buffer in use");
} else if ((p = checkstring(cmdline, (unsigned char*)"WRITE"))) {
int mode = 0;
getargs(&p, 7, (unsigned char*)",");
if (!(argc == 5 || argc == 7)) error((char *)"Syntax");
if (*argv[0] == '#') argv[0]++;
bnbr = (int)getint(argv[0], 1, MAXBLITBUF)-1; // get the number
if(blitbuff[bnbr].h==9999)error("Invalid buffer");
if (blitbuff[bnbr].blitbuffptr != NULL) {
x1 = (int)getint(argv[2], -blitbuff[bnbr].w + 1, HRes);
y1 = (int)getint(argv[4], -blitbuff[bnbr].h + 1, VRes);
if (argc == 7)mode = (char)getint(argv[6], 0, 7);
w = blitbuff[bnbr].w;
h = blitbuff[bnbr].h;
// int cursorhidden = 0;
int rotation = mode & 3;
if(x1>=HRes || x1+w<0 || y1>=VRes || y1+h<0)return;
if(x1>=0 && mode==0 && x1+w<=HRes)buff=(unsigned char *)blitbuff[bnbr].blitbuffptr;
else {
buff=GetTempMemory(w*h*4);
for(int j=w*h*4-1,i=w*h*3-1;j>=0;j-=4){
buff[j]=0;
buff[j-1]=blitbuff[bnbr].blitbuffptr[i--];
buff[j-2]=blitbuff[bnbr].blitbuffptr[i--];
buff[j-3]=blitbuff[bnbr].blitbuffptr[i--];
}
int *d=(int *)buff;
if(rotation & 1){ //swap left/write
for (int y = 0; y < h; y++) {
for (int x = 0,xx=w-1; x < (w>>1); x++,xx--) {
swap(d[y*w+x],d[y*w+xx]);
}
}
}
if(rotation & 2){
for(int x=0;x<w;x++){
for(int y=0,yy=h-1;y<(h>>1);y++,yy--){
swap(d[x+y*w],d[x+yy*w]);
}
}
}
if(x1<0){ //now deal with situation where you are blitting part off the left of the screen
int *s=(int *)buff;
d=(int *)buff;
int start=-x1;
for(int y=0;y<h;y++){
for(int x=0;x<w;x++){
if(x>=start)*d++=*s++;
else s++;
}
}
w-=start;
x1=0;
}
if(x1+w>=HRes){ //now deal with situation where you are blitting part off the right of the screen
int *s=(int *)buff;
d=(int *)buff;
int over=((x1+w)-HRes);
int end=w-over;
for(int y=0;y<h;y++){
for(int x=0;x<w;x++){
if(x>=end)s++;
else *d++=*s++;
}
}
w-=over;
}
for(int i=0,j=0;i<w*h*3;i+=3){
buff[i]=buff[j++];
buff[i+1]=buff[j++];
buff[i+2]=buff[j++];
j++;
}
}
if(!(mode & 4)){
if(y1<0){
buff-=(y1*3*w);
h+=y1;
y1=0;
}
DrawBuffer(x1, y1, x1 + w - 1, y1 + h - 1, buff);
} else {
if((void *)ReadBuffer == (void *)DisplayNotSet) error("Invalid on this display");
unsigned char *current=GetTempMemory(w*h*3);
if(y1<0){
buff-=(y1*3*w);
h+=y1;
y1=0;
}
ReadBuffer(x1, y1, x1 + w - 1, y1 + h - 1, current);
for(int i=0;i<w*h*3;i+=3){
if(buff[i] || buff[i+1] || buff[i+2]){
current[i]=buff[i];
current[i+1]=buff[i+1];
current[i+2]=buff[i+2];
}
}
DrawBuffer(x1, y1, x1 + w - 1, y1 + h - 1, current);
}
}
else error((char *)"Buffer not in use");
} else if((p = checkstring(cmdline, (unsigned char *)"CLOSE"))) {
getargs(&p, 1, (unsigned char *)",");
if(*argv[0] == '#') argv[0]++; // check if the first arg is prefixed with a #
bnbr = getint(argv[0], 1, MAXBLITBUF) - 1; // get the buffer number
if(blitbuff[bnbr].blitbuffptr != NULL){
FreeMemory((unsigned char *)blitbuff[bnbr].blitbuffptr);
blitbuff[bnbr].blitbuffptr = NULL;
} else error("Buffer not in use");
// get the number
} else {
getargs(&cmdline, 11, (unsigned char *)",");
if((void *)ReadBuffer == (void *)DisplayNotSet) error("Invalid on this display");
if(argc != 11) error("Syntax");
x1 = getinteger(argv[0]);
y1 = getinteger(argv[2]);
x2 = getinteger(argv[4]);
y2 = getinteger(argv[6]);
w = getinteger(argv[8]);
h = getinteger(argv[10]);
if(w < 1 || h < 1) return;
if(x1 < 0) { x2 -= x1; w += x1; x1 = 0; }
if(x2 < 0) { x1 -= x2; w += x2; x2 = 0; }
if(y1 < 0) { y2 -= y1; h += y1; y1 = 0; }
if(y2 < 0) { y1 -= y2; h += y2; y2 = 0; }
if(x1 + w > HRes) w = HRes - x1;
if(x2 + w > HRes) w = HRes - x2;
if(y1 + h > VRes) h = VRes - y1;
if(y2 + h > VRes) h = VRes - y2;
if(w < 1 || h < 1 || x1 < 0 || x1 + w > HRes || x2 < 0 || x2 + w > HRes || y1 < 0 || y1 + h > VRes || y2 < 0 || y2 + h > VRes) return;
#ifdef PICOMITEVGA
if(DISPLAY_TYPE==SCREENMODE2 || DISPLAY_TYPE==SCREENMODE3){
if((w & 1)==0 && (x1 & 1)==0 && (x2 & 1)==0){ //Easiest case - byte move in the x direction with w even
if(y1<y2){
for(int y=h-1; y>=0;y--){
volatile uint8_t *in=WriteBuf + ((y+y1)*HRes + x1)/2;
volatile uint8_t *out=WriteBuf + ((y+y2)*HRes + x2)/2;
memcpy((void *)out,(void *)in,w/2);
}
} else if(y1>y2){
for(int y=0;y<h;y++){
volatile uint8_t *in=WriteBuf + ((y+y1)*HRes + x1)/2;
volatile uint8_t *out=WriteBuf + ((y+y2)*HRes + x2)/2;
memcpy((void *)out,(void *)in,w/2);
}
} else {
for(int y=0;y<h;y++){
volatile uint8_t *in=WriteBuf + ((y+y1)*HRes + x1)/2;
volatile uint8_t *out=WriteBuf + ((y+y2)*HRes + x2)/2;
memmove((void *)out,(void *)in,w/2);
}
}
return;
} else { //nibble move not as easy
uint8_t *inbuff=GetTempMemory(HRes/2);
int intoggle=x1 & 1;
int outtoggle=x2 & 1;
int n=w/2;
if(w & 1)n++;
if(y1>y2){
for(int y=0;y<h;y++){
if(!intoggle)memcpy(inbuff,(void *)WriteBuf + ((y+y1)*HRes + x1)/2, n);
else {
int toggle=1;
volatile uint8_t *in=WriteBuf + ((y+y1)*HRes + x1)/2;
volatile uint8_t *out=inbuff;
for(int x=0;x<w;x++){
if(toggle){
uint8_t t=*in >>4 ;
*out =t ;
in++;
} else {
uint8_t t=(*in & 0xf)<<4;
*out|= t;
out++;
}
toggle ^=1;
}
}
if(!outtoggle){
memcpy((void *)WriteBuf + ((y+y2)*HRes + x2)/2, inbuff, w/2);
if(w & 1){
volatile uint8_t *lastnibble=WriteBuf + ((y+y2) * HRes + x2 + w)/2;
*lastnibble &= 0xf0;
*lastnibble |= (inbuff[w/2] & 0xf);
}
} else {
int toggle=1;
volatile uint8_t *in=inbuff;
volatile uint8_t *out=WriteBuf + ((y+y2) * HRes + x2)/2;
for(int x=0;x<w;x++){
if(toggle){
uint8_t t=(*in & 0xf)<<4;
*out &=0x0f; //clear the top byte of the output
*out |=t;
out++;
} else {
uint8_t t=(*in >>4);
*out &=0xf0;
*out|= t;
in++;
}
toggle ^=1;
}
}
}
} else {
for(int y=h-1;y>=0;y--){
if(!intoggle)memcpy(inbuff,(void *)WriteBuf + ((y+y1)*HRes + x1)/2, n);
else {
int toggle=1;
volatile uint8_t *in=WriteBuf + ((y+y1)*HRes + x1)/2;
volatile uint8_t *out=inbuff;
for(int x=0;x<w;x++){
if(toggle){
uint8_t t=*in >>4 ;
*out =t ;
in++;
} else {
uint8_t t=(*in & 0xf)<<4;
*out|= t;
out++;
}
toggle ^=1;
}
}
if(!outtoggle){
memcpy((void *)WriteBuf + ((y+y2)*HRes + x2)/2, inbuff, w/2);
if(w & 1){
volatile uint8_t *lastnibble=WriteBuf + ((y+y2) * HRes + x2 + w)/2;
*lastnibble &= 0xf0;
*lastnibble |= (inbuff[w/2] & 0xf);
}
} else {
int toggle=1;
volatile uint8_t *in=inbuff;
volatile uint8_t *out=WriteBuf + ((y+y2) * HRes + x2)/2;
for(int x=0;x<w;x++){
if(toggle){
uint8_t t=(*in & 0xf)<<4;
*out &=0x0f; //clear the top byte of the output
*out |=t;
out++;
} else {
uint8_t t=(*in >>4);
*out &=0xf0;
*out|= t;
in++;
}
toggle ^=1;
}
}
}
}
return;
}
} else if(DISPLAY_TYPE && (DISPLAY_TYPE==SCREENMODE4 || DISPLAY_TYPE==SCREENMODE5)){
unsigned char *buff = NULL;
int max_x;
if(x1 >= x2) {
max_x = 1;
buff = GetMemory((max_x * h) * (DISPLAY_TYPE==SCREENMODE4 ? 2 : 1));
while(w > max_x){
ReadBufferFast(x1, y1, x1 + max_x - 1, y1 + h - 1, buff);
DrawBufferFast(x2, y2, x2 + max_x - 1, y2 + h - 1, -1, buff);
x1 += max_x;
x2 += max_x;
w -= max_x;
}
ReadBufferFast(x1, y1, x1 + w - 1, y1 + h - 1, buff);
DrawBufferFast(x2, y2, x2 + w - 1, y2 + h - 1, -1, buff);
FreeMemory(buff);
}
if(x1 < x2) {
int start_x1, start_x2;
max_x = 1;
buff = GetMemory((max_x * h) * (DISPLAY_TYPE==SCREENMODE4 ? 2 : 1));
start_x1 = x1 + w - max_x;
start_x2 = x2 + w - max_x;
while(w > max_x){
ReadBufferFast(start_x1, y1, start_x1 + max_x - 1, y1 + h - 1, buff);
DrawBufferFast(start_x2, y2, start_x2 + max_x - 1, y2 + h - 1, -1, buff);
w -= max_x;
start_x1 -= max_x;
start_x2 -= max_x;
}
ReadBufferFast(x1, y1, x1 + w - 1, y1 + h - 1, buff);
DrawBufferFast(x2, y2, x2 + w - 1, y2 + h - 1, -1, buff);
FreeMemory(buff);
}
} else if(DISPLAY_TYPE && DISPLAY_TYPE==SCREENMODE1){
unsigned char *buff = NULL;
int max_x, ww;
ww=w;
if(x1 >= x2) {
max_x = 1;
buff = GetMemory((max_x * h)>>1);
while(w > max_x){
ReadBufferFast(x1, y1, x1 + max_x - 1, y1 + h - 1, buff);
DrawBufferFast(x2, y2, x2 + max_x - 1, y2 + h - 1, -1, buff);
x1 += max_x;
x2 += max_x;
w -= max_x;
}
ReadBufferFast(x1, y1, x1 + w - 1, y1 + h - 1, buff);
DrawBufferFast(x2, y2, x2 + w - 1, y2 + h - 1, -1, buff);
FreeMemory(buff);
if((x1 % 8==0) && (x2 % 8 ==0) && (y1 % ytileheight==0) && (y2 % ytileheight ==0) && (ww % 8==0) && (h % ytileheight==0)){
int tx1=x1/8;
int xc=ww/8;
int ty1=y1/ytileheight;
int yc=h/ytileheight;
int tx2=x2/8;
int ty2=y2/ytileheight;
for (int x=0;x<xc;x++){
for(int y=0;y<yc;y++){
int s=(y+ty1)*X_TILE+x+tx1;
int d=(y+ty2)*X_TILE+x+tx2;
tilefcols[d]=tilefcols[s];
tilebcols[d]=tilebcols[s];
}
}
}
return;
}
if(x1 < x2) {
int start_x1, start_x2;
max_x = 1;
buff = GetMemory(max_x * h);
start_x1 = x1 + w - max_x;
start_x2 = x2 + w - max_x;
while(w > max_x){
ReadBufferFast(start_x1, y1, start_x1 + max_x - 1, y1 + h - 1, buff);
DrawBufferFast(start_x2, y2, start_x2 + max_x - 1, y2 + h - 1, -1, buff);
w -= max_x;
start_x1 -= max_x;
start_x2 -= max_x;
}
ReadBufferFast(x1, y1, x1 + w - 1, y1 + h - 1, buff);
DrawBufferFast(x2, y2, x2 + w - 1, y2 + h - 1, -1, buff);
FreeMemory(buff);
if((x1 % 8==0) && (x2 % 8 ==0) && (y1 % ytileheight==0) && (y2 % ytileheight ==0) && (ww % 8==0) && (h % ytileheight==0)){
int tx1=x1/8;
int xc=ww/8;
int ty1=y1/ytileheight;
int yc=h/ytileheight;
int tx2=x2/8;
int ty2=y2/ytileheight;
for (int x=xc-1;x>=0;x--){
for(int y=0;y<yc;y++){
int s=(y+ty1)*X_TILE+x+tx1;
int d=(y+ty2)*X_TILE+x+tx2;
tilefcols[d]=tilefcols[s];
tilebcols[d]=tilebcols[s];
}
}
}
return;
}
}
}
#else
int max_x;
if((WriteBuf==LayerBuf || WriteBuf==FrameBuf) && WriteBuf){
if((w & 1)==0 && (x1 & 1)==0 && (x2 & 1)==0){ //Easiest case - byte move in the x direction with w even
if(y1<y2){
for(int y=h-1; y>=0;y--){
uint8_t *in=WriteBuf + ((y+y1)*HRes + x1)/2;
uint8_t *out=WriteBuf + ((y+y2)*HRes + x2)/2;
memcpy(out,in,w/2);
}
} else if(y1>y2){
for(int y=0;y<h;y++){
uint8_t *in=WriteBuf + ((y+y1)*HRes + x1)/2;
uint8_t *out=WriteBuf + ((y+y2)*HRes + x2)/2;
memcpy(out,in,w/2);
}
} else {
for(int y=0;y<h;y++){
uint8_t *in=WriteBuf + ((y+y1)*HRes + x1)/2;
uint8_t *out=WriteBuf + ((y+y2)*HRes + x2)/2;
memmove(out,in,w/2);
}
}
return;
} else { //nibble move not as easy
uint8_t *inbuff=GetTempMemory(HRes/2);
int intoggle=x1 & 1;
int outtoggle=x2 & 1;
int n=w/2;
if(w & 1)n++;
if(y1>y2){
for(int y=0;y<h;y++){
if(!intoggle)memcpy(inbuff,WriteBuf + ((y+y1)*HRes + x1)/2, n);
else {
int toggle=1;
uint8_t *in=WriteBuf + ((y+y1)*HRes + x1)/2;
uint8_t *out=inbuff;
for(int x=0;x<w;x++){
if(toggle){
uint8_t t=*in >>4 ;
*out =t ;
in++;
} else {
uint8_t t=(*in & 0xf)<<4;
*out|= t;
out++;
}
toggle ^=1;
}
}
if(!outtoggle){
memcpy(WriteBuf + ((y+y2)*HRes + x2)/2, inbuff, w/2);
if(w & 1){
uint8_t *lastnibble=WriteBuf + ((y+y2) * HRes + x2 + w)/2;
*lastnibble &= 0xf0;
*lastnibble |= (inbuff[w/2] & 0xf);
}
} else {
int toggle=1;
uint8_t *in=inbuff;
uint8_t *out=WriteBuf + ((y+y2) * HRes + x2)/2;
for(int x=0;x<w;x++){
if(toggle){
uint8_t t=(*in & 0xf)<<4;
*out &=0x0f; //clear the top byte of the output
*out |=t;
out++;
} else {
uint8_t t=(*in >>4);
*out &=0xf0;
*out|= t;
in++;
}
toggle ^=1;
}
}
}
} else {
for(int y=h-1;y>=0;y--){
if(!intoggle)memcpy(inbuff,WriteBuf + ((y+y1)*HRes + x1)/2, n);
else {
int toggle=1;
uint8_t *in=WriteBuf + ((y+y1)*HRes + x1)/2;
uint8_t *out=inbuff;
for(int x=0;x<w;x++){
if(toggle){
uint8_t t=*in >>4 ;
*out =t ;
in++;
} else {
uint8_t t=(*in & 0xf)<<4;
*out|= t;
out++;
}
toggle ^=1;
}
}
if(!outtoggle){
memcpy(WriteBuf + ((y+y2)*HRes + x2)/2, inbuff, w/2);
if(w & 1){
uint8_t *lastnibble=WriteBuf + ((y+y2) * HRes + x2 + w)/2;
*lastnibble &= 0xf0;
*lastnibble |= (inbuff[w/2] & 0xf);
}
} else {
int toggle=1;
uint8_t *in=inbuff;
uint8_t *out=WriteBuf + ((y+y2) * HRes + x2)/2;
for(int x=0;x<w;x++){
if(toggle){
uint8_t t=(*in & 0xf)<<4;
*out &=0x0f; //clear the top byte of the output
*out |=t;
out++;
} else {
uint8_t t=(*in >>4);
*out &=0xf0;
*out|= t;
in++;
}
toggle ^=1;
}
}
}
}
return;
}
} else {
if(x1 >= x2) {
max_x = 1;
buff = GetMemory(max_x * h * (SSD16TYPE || Option.DISPLAY_TYPE==IPS_4_16 ? 2 : 3));
while(w > max_x){
ReadBLITBuffer(x1, y1, x1 + max_x - 1, y1 + h - 1, buff);
DrawBLITBuffer(x2, y2, x2 + max_x - 1, y2 + h - 1, buff);
x1 += max_x;
x2 += max_x;
w -= max_x;
}
ReadBLITBuffer(x1, y1, x1 + w - 1, y1 + h - 1, buff);
DrawBLITBuffer(x2, y2, x2 + w - 1, y2 + h - 1, buff);
FreeMemory(buff);
return;
}
if(x1 < x2) {
int start_x1, start_x2;
max_x = LargestContiguousHeap()/(SSD16TYPE || Option.DISPLAY_TYPE==IPS_4_16 ? 2 : 3);
if(max_x>x2-x1+1)max_x=x2-x1+1;
buff = GetMemory(max_x * h * (SSD16TYPE || Option.DISPLAY_TYPE==IPS_4_16 ? 2 : 3));
start_x1 = x1 + w - max_x;
start_x2 = x2 + w - max_x;
while(w > max_x){
ReadBLITBuffer(start_x1, y1, start_x1 + max_x - 1, y1 + h - 1, buff);
DrawBLITBuffer(start_x2, y2, start_x2 + max_x - 1, y2 + h - 1, buff);
w -= max_x;
start_x1 -= max_x;
start_x2 -= max_x;
}
ReadBLITBuffer(x1, y1, x1 + w - 1, y1 + h - 1, buff);
DrawBLITBuffer(x2, y2, x2 + w - 1, y2 + h - 1, buff);
FreeMemory(buff);
if(Option.Refresh)Display_Refresh();
return;
}
}
}
#endif
}
void MIPS16 cmd_font(void) {
getargs(&cmdline, 3, (unsigned char *)",");
if(argc < 1) error("Argument count");
if(*argv[0] == '#') ++argv[0];
if(argc == 3)
SetFont(((getint(argv[0], 1, FONT_TABLE_SIZE) - 1) << 4) | getint(argv[2], 1, 15));
else
SetFont(((getint(argv[0], 1, FONT_TABLE_SIZE) - 1) << 4) | 1);
if(Option.DISPLAY_CONSOLE && !CurrentLinePtr) { // if we are at the command prompt on the LCD
#ifdef PICOMITEVGA
if(gui_font_height>=8 && (gui_font_width % 8)==0){
ytileheight=gui_font_height;
Y_TILE=(VRes+ytileheight-1)/ytileheight;
for(int i=0;i<X_TILE*Y_TILE;i++){
#if defined(rp2350) && defined(HDMI)
if(FullColour){
tilefcols[i]=tilefcols[0];
tilebcols[i]=tilebcols[0];
} else {
tilefcols_w[i]=tilefcols_w[0];
tilebcols_w[i]=tilebcols_w[0];
}
#else
tilefcols[i]=tilefcols[0];
tilebcols[i]=tilebcols[0];
#endif
}
}
#endif
PromptFont = gui_font;
if(CurrentY + gui_font_height >= VRes) {
ScrollLCD(CurrentY + gui_font_height - VRes); // scroll up if the font change split the line over the bottom
CurrentY -= (CurrentY + gui_font_height - VRes);
}
}
}
#ifdef PICOMITEVGA
void cmd_colourmap(void){
long long int *cptr=NULL, *fptr=NULL;
MMFLOAT *cfptr=NULL, *ffptr=NULL;
int nf,n,i;
int map[16];
getargs(&cmdline,5,(unsigned char *)",");
memcpy((void *)map,(void *)RGB121map,16*sizeof(int));
if(!(argc==3 || argc==5))error("Argument count");
n=parsenumberarray(argv[0],&cfptr,&cptr,1,1,NULL,true);
if(argc==5){ //user defined mapping
MMFLOAT* a3float = NULL;
int64_t* a3int = NULL;
if(parsenumberarray(argv[4],&a3float,&a3int,3,1,NULL,true)!=16)error("Array size not 16 elements");
if(a3int!=NULL){
for(i=0;i<16;i++) {
map[i]=a3int[i];
if(map[i]<0 || map[i]>0xFFFFFF)error("Invalid colour");
}
} else {
for(i=0;i<16;i++) {
map[i]=a3float[i];
if(map[i]<0 || map[i]>0xFFFFFF)error("Invalid colour");
}
}
}
nf=parsenumberarray(argv[2],&ffptr,&fptr,1,1,NULL,false);
if(nf!=n)error("Array size mismatch %, %",n,nf);
for(int i=0;i<n;i++){
int in=(cptr == NULL ? (int)cfptr[i] : cptr[i]);
if(in>=16)error("Input range error on element %",i);
if(fptr==NULL)ffptr[i]=map[in];
else fptr[i]=map[in];
}
}
#endif
void cmd_colour(void) {
getargs(&cmdline, 3, (unsigned char *)",");
if(argc < 1) error("Argument count");
gui_fcolour = getColour((char *)argv[0], 0);
if(argc == 3) gui_bcolour = getColour((char *)argv[2], 0);
last_fcolour = gui_fcolour;
last_bcolour = gui_bcolour;
if(!CurrentLinePtr) {
PromptFC = gui_fcolour;
PromptBC = gui_bcolour;
}
}
#ifdef PICOMITEVGA
void fun_map(void){
int cl=getint(ep,0,255);
switch(DISPLAY_TYPE){
case SCREENMODE1:
case SCREENMODE4:
error("Invalid for Mode");
break;
case SCREENMODE2:
case SCREENMODE3:
if(cl>15)error("Mode has 16 colours - 0 to 15");
targ=T_INT;
iret=((cl & 0b1000)<<20) | ((cl & 0b110)<<13) | ((cl & 0b1)<<7);
break;
case SCREENMODE5:
targ=T_INT;
iret=((cl & 0b11100000)<<16) | ((cl & 0b00011100)<<11) | ((cl & 0b11)<<6);
break;
}
}
#ifndef HDMI
void cmd_map(void){
unsigned char *p;
// if(Option.CPU_Speed==126000)error("CPUSPEED >= 252000 for colour mapping");
if(!(DISPLAY_TYPE==SCREENMODE2 || DISPLAY_TYPE==SCREENMODE3 ))error("Invalid for this screen mode");
if((p=checkstring(cmdline, (unsigned char *)"RESET"))) {
while(QVgaScanLine!=0){}
for(int i=0;i<16;i++)remap[i]=RGB121map[i];
for(int i=0;i<16;i++)map16[i]=RGB121(remap[i]);
} else if((p=checkstring(cmdline, (unsigned char *)"MAXIMITE"))) {
while(QVgaScanLine!=0){}
for(int i=0;i<16;i++)remap[i]=CMM1map[i];
for(int i=0;i<16;i++)map16[i]=RGB121(remap[i]);
} else if((p=checkstring(cmdline, (unsigned char *)"SET"))) {
while(QVgaScanLine!=0){}
for(int i=0;i<16;i++)map16[i]=RGB121(remap[i]);
} else {
static bool first=true;
int cl = getinteger(cmdline);
while(*cmdline && tokenfunction(*cmdline) != op_equal) cmdline++;
if(!*cmdline) error("Invalid syntax");
++cmdline;
if(!*cmdline) error("Invalid syntax");
int col=getColour((char *)cmdline,0);
if(first){
for(int i=0;i<16;i++)remap[i]=RGB121map[i];
first=false;
}
remap[cl]=col;
}
}
void cmd_tile(void){
unsigned char *tp;
uint32_t bcolour=0xFFFFFFFF,fcolour=0xFFFFFFFF;
int xlen=1,ylen=1;
if(DISPLAY_TYPE!=SCREENMODE1)error("Invalid for this screen mode");
if(checkstring(cmdline,(unsigned char *)"RESET")){
for(int x=0;x<X_TILE;x++){
for(int y=0;y<Y_TILE;y++){
tilefcols[y*X_TILE+x]=RGB121pack(gui_fcolour);
tilebcols[y*X_TILE+x]=RGB121pack(gui_bcolour);
}
}
} else if((tp=checkstring(cmdline,(unsigned char *)"HEIGHT"))){
if(!(WriteBuf==DisplayBuf))error("Not available when write is set to a buffer");
ytileheight=getint(tp,12,VRes);
Y_TILE=VRes/ytileheight;
if(VRes % ytileheight)Y_TILE++;
ClearScreen(Option.DefaultBC);
} else {
getargs(&cmdline, 11, (unsigned char *)",");
if(!(DISPLAY_TYPE==SCREENMODE1))return;
if(argc<5)error("Syntax");
int x=getint(argv[0],0,X_TILE);
int y=getint(argv[2],0,Y_TILE);
int tilebcolour, tilefcolour ;
if(*argv[4]){
tilefcolour = getColour((char *)argv[4], 0);
fcolour = RGB121pack(tilefcolour);
}
if(argc>=7 && *argv[6]){
tilebcolour = getColour((char *)argv[6], 0);
bcolour = RGB121pack(tilebcolour);
}
if(argc>=9 && *argv[8]){
xlen=getint(argv[8],1,X_TILE-x);
}
if(argc>=11 && *argv[10]){
ylen=getint(argv[10],1,Y_TILE-y);
}
for(int xp=x;xp<x+xlen;xp++){
for(int yp=y;yp<y+ylen;yp++){
if(fcolour!=0xFFFFFFFF) tilefcols[yp*X_TILE+xp]=(uint16_t)fcolour;
if(bcolour!=0xFFFFFFFF) tilebcols[yp*X_TILE+xp]=(uint16_t)bcolour;
}
}
}
}
#else
/*
* @cond
* The following section will be excluded from the documentation.
*/
void DrawRectangle555(int x1, int y1, int x2, int y2, int c){
int x,y,t;
uint16_t col=((c & 0xf8)>>3) | ((c& 0xf800)>>6) | ((c & 0xf80000)>>9);
if(x1 < 0) x1 = 0;
if(x1 >= HRes) x1 = HRes - 1;
if(x2 < 0) x2 = 0;
if(x2 >= HRes) x2 = HRes - 1;
if(y1 < 0) y1 = 0;
if(y1 >= VRes) y1 = VRes - 1;
if(y2 < 0) y2 = 0;
if(y2 >= VRes) y2 = VRes - 1;
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
for(y=y1;y<=y2;y++){
uint16_t *p=(uint16_t *)((uint8_t *)(WriteBuf+((y*HRes+x1)*2)));
for(x=x1;x<=x2;x++){
*p++=col;
}
}
}
void DrawBitmap555(int x1, int y1, int width, int height, int scale, int fc, int bc, unsigned char *bitmap){
int i, j, k, m, x, y;
// unsigned char mask;
if(x1>=HRes || y1>=VRes || x1+width*scale<0 || y1+height*scale<0)return;
uint16_t fcolour = RGB555(fc);
uint16_t bcolour = RGB555(bc);
for(i = 0; i < height; i++) { // step thru the font scan line by line
for(j = 0; j < scale; j++) { // repeat lines to scale the font
for(k = 0; k < width; k++) { // step through each bit in a scan line
for(m = 0; m < scale; m++) { // repeat pixels to scale in the x axis
x=x1 + k * scale + m ;
y=y1 + i * scale + j ;
if(x >= 0 && x < HRes && y >= 0 && y < VRes) { // if the coordinates are valid
uint16_t *p=(uint16_t *)(((uint32_t) WriteBuf)+(y*(HRes<<1))+(x<<1));
if((bitmap[((i * width) + k)/8] >> (((height * width) - ((i * width) + k) - 1) %8)) & 1) {
*p=fcolour;
} else {
if(bc>=0){
*p=bcolour;
}
}
}
}
}
}
}
}
void DrawBuffer555(int x1, int y1, int x2, int y2, unsigned char *p){
int x,y, t;
union colourmap
{
char rgbbytes[4];
unsigned int rgb;
} c;
uint16_t fcolour;
uint16_t *pp;
// make sure the coordinates are kept within the display area
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
if(x1 < 0) x1 = 0;
if(x1 >= HRes) x1 = HRes - 1;
if(x2 < 0) x2 = 0;
if(x2 >= HRes) x2 = HRes - 1;
if(y1 < 0) y1 = 0;
if(y1 >= VRes) y1 = VRes - 1;
if(y2 < 0) y2 = 0;
if(y2 >= VRes) y2 = VRes - 1;
for(y=y1;y<=y2;y++){
for(x=x1;x<=x2;x++){
c.rgbbytes[0]=*p++; //this order swaps the bytes to match the .BMP file
c.rgbbytes[1]=*p++;
c.rgbbytes[2]=*p++;
fcolour = RGB555(c.rgb);
pp=(uint16_t *)(((uint32_t) WriteBuf)+(y*(HRes<<1))+(x<<1));
*pp=fcolour;
}
}
}
void DrawBuffer555Fast(int x1, int y1, int x2, int y2, int blank, unsigned char *p){
int x,y,t;
uint16_t c;
uint16_t *pp, *qq=(uint16_t *)p;
// make sure the coordinates are kept within the display area
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
for(y=y1;y<=y2;y++){
for(x=x1;x<=x2;x++){
if(x>=0 && x<HRes && y>=0 && y<VRes){
pp=(uint16_t *)(WriteBuf+(y*(HRes<<1))+(x<<1));
c=*qq++;
if(c!=sprite_transparent || blank==-1)*pp = c;
}
}
}
}
void DrawPixel555(int x, int y, int c){
if(x<0 || y<0 || x>=HRes || y>=VRes)return;
uint16_t colour = RGB555(c);
uint16_t *p=(uint16_t *)(((uint32_t) WriteBuf)+(y*(HRes<<1))+(x<<1));
*p=colour;
}
void ReadBuffer555(int x1, int y1, int x2, int y2, unsigned char *c){
int x,y,t;
uint16_t *pp;
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
int xx1=x1, yy1=y1, xx2=x2, yy2=y2;
if(x1 < 0) xx1 = 0;
if(x1 >= HRes) xx1 = HRes - 1;
if(x2 < 0) xx2 = 0;
if(x2 >= HRes) xx2 = HRes - 1;
if(y1 < 0) yy1 = 0;
if(y1 >= VRes) yy1 = VRes - 1;
if(y2 < 0) yy2 = 0;
if(y2 >= VRes) yy2 = VRes - 1;
for(y=yy1;y<=yy2;y++){
for(x=xx1;x<=xx2;x++){
pp=(uint16_t *)(((uint32_t) WriteBuf)+(y*(HRes<<1))+(x<<1));
t=*pp;
*c++=((t&0x1F)<<3);
*c++=(((t>>5)&0x1F)<<3);
*c++=(((t>>10)&0x1F)<<3);
}
}
}
void ReadBuffer555Fast(int x1, int y1, int x2, int y2, unsigned char *c){
int x,y,t;
uint16_t *pp, *qq=(uint16_t *)c;
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
int xx1=x1, yy1=y1, xx2=x2, yy2=y2;
if(x1 < 0) xx1 = 0;
if(x1 >= HRes) xx1 = HRes - 1;
if(x2 < 0) xx2 = 0;
if(x2 >= HRes) xx2 = HRes - 1;
if(y1 < 0) yy1 = 0;
if(y1 >= VRes) yy1 = VRes - 1;
if(y2 < 0) yy2 = 0;
if(y2 >= VRes) yy2 = VRes - 1;
for(y=yy1;y<=yy2;y++){
for(x=xx1;x<=xx2;x++){
pp=(uint16_t *)(((uint32_t) WriteBuf)+(y*(HRes<<1))+(x<<1));
*qq++=*pp;
}
}
}
void ScrollLCD555(int lines){
if(lines==0)return;
if(lines >= 0) {
for(int i=0;i<VRes-lines;i++) {
int d=i*(HRes<<1),s=(i+lines)*(HRes<<1);
for(int c=0;c<(HRes<<1);c++)WriteBuf[d+c]=WriteBuf[s+c];
}
DrawRectangle(0, VRes-lines, HRes - 1, VRes - 1, PromptBC); // erase the lines to be scrolled off
} else {
lines=-lines;
for(int i=VRes-1;i>=lines;i--) {
int d=i*(HRes<<1),s=(i-lines)*(HRes<<1);
for(int c=0;c<(HRes<<1);c++)WriteBuf[d+c]=WriteBuf[s+c];
}
DrawRectangle(0, 0, HRes - 1, lines - 1, PromptBC); // erase the lines introduced at the top
}
}
void DrawRectangle256(int x1, int y1, int x2, int y2, int c){
int y,t;
uint8_t colour =RGB332(c);
if(x1 < 0) x1 = 0;
if(x1 >= HRes) x1 = HRes - 1;
if(x2 < 0) x2 = 0;
if(x2 >= HRes) x2 = HRes - 1;
if(y1 < 0) y1 = 0;
if(y1 >= VRes) y1 = VRes - 1;
if(y2 < 0) y2 = 0;
if(y2 >= VRes) y2 = VRes - 1;
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
for(y=y1;y<=y2;y++){
volatile uint8_t *p=WriteBuf+(y*HRes+x1);
memset((void *)p,colour,x2-x1+1);
}
}
void DrawBitmap256(int x1, int y1, int width, int height, int scale, int fc, int bc, unsigned char *bitmap){
int i, j, k, m, x, y;
// unsigned char mask;
if(x1>=HRes || y1>=VRes || x1+width*scale<0 || y1+height*scale<0)return;
uint8_t fcolour = RGB332(fc);
uint8_t bcolour = RGB332(bc);
for(i = 0; i < height; i++) { // step thru the font scan line by line
for(j = 0; j < scale; j++) { // repeat lines to scale the font
for(k = 0; k < width; k++) { // step through each bit in a scan line
for(m = 0; m < scale; m++) { // repeat pixels to scale in the x axis
x=x1 + k * scale + m ;
y=y1 + i * scale + j ;
if(x >= 0 && x < HRes && y >= 0 && y < VRes) { // if the coordinates are valid
uint8_t *p=(uint8_t *)((uint32_t)(WriteBuf+y*HRes+x));
if((bitmap[((i * width) + k)/8] >> (((height * width) - ((i * width) + k) - 1) %8)) & 1) {
*p=fcolour;
} else {
if(bc>=0){
*p=bcolour;
}
}
}
}
}
}
}
}
void DrawBuffer256(int x1, int y1, int x2, int y2, unsigned char *p){
int x,y, t;
union colourmap
{
char rgbbytes[4];
unsigned int rgb;
} c;
uint8_t fcolour;
uint8_t *pp;
// make sure the coordinates are kept within the display area
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
if(x1 < 0) x1 = 0;
if(x1 >= HRes) x1 = HRes - 1;
if(x2 < 0) x2 = 0;
if(x2 >= HRes) x2 = HRes - 1;
if(y1 < 0) y1 = 0;
if(y1 >= VRes) y1 = VRes - 1;
if(y2 < 0) y2 = 0;
if(y2 >= VRes) y2 = VRes - 1;
for(y=y1;y<=y2;y++){
for(x=x1;x<=x2;x++){
c.rgbbytes[0]=*p++; //this order swaps the bytes to match the .BMP file
c.rgbbytes[1]=*p++;
c.rgbbytes[2]=*p++;
fcolour = RGB332(c.rgb);
pp=(uint8_t *)((uint32_t)(WriteBuf+y*HRes+x));
*pp=fcolour;
}
}
}
void DrawBuffer256Fast(int x1, int y1, int x2, int y2, int blank, unsigned char *p){
int x,y,t;
uint8_t c;
uint8_t *pp, *qq=(uint8_t *)p;
// make sure the coordinates are kept within the display area
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
for(y=y1;y<=y2;y++){
for(x=x1;x<=x2;x++){
if(x>=0 && x<HRes && y>=0 && y<VRes){
pp=(uint8_t *)((uint32_t)(WriteBuf+y*HRes+x));
c=*qq++;
if(c!=sprite_transparent || blank==-1)*pp = c;
}
}
}
}
void DrawPixel256(int x, int y, int c){
if(x<0 || y<0 || x>=HRes || y>=VRes)return;
uint8_t colour = RGB332(c);
uint8_t *p=(uint8_t *)((uint32_t)(WriteBuf+y*HRes+x));
*p=colour;
}
void ReadBuffer256(int x1, int y1, int x2, int y2, unsigned char *c){
int x,y,t;
uint8_t *pp;
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
int xx1=x1, yy1=y1, xx2=x2, yy2=y2;
if(x1 < 0) xx1 = 0;
if(x1 >= HRes) xx1 = HRes - 1;
if(x2 < 0) xx2 = 0;
if(x2 >= HRes) xx2 = HRes - 1;
if(y1 < 0) yy1 = 0;
if(y1 >= VRes) yy1 = VRes - 1;
if(y2 < 0) yy2 = 0;
if(y2 >= VRes) yy2 = VRes - 1;
for(y=yy1;y<=yy2;y++){
for(x=xx1;x<=xx2;x++){
pp=(uint8_t *)((uint32_t)(WriteBuf+y*HRes+x));
#ifdef PICOMITEVGA
unsigned int q;
uint8_t *qq=pp;
if(WriteBuf==DisplayBuf && LayerBuf != DisplayBuf && LayerBuf !=NULL)qq=(uint8_t *)((uint32_t)(LayerBuf+y*HRes+x));
#endif
t=*pp;
#ifdef PICOMITEVGA
q=*qq;
if(!(*qq==transparent) && mergedread)t=q;
#endif
*c++=((t & 0x3)<<6);
*c++=(((t>>2) & 0x7)<<5);
*c++=(((t>>5) & 0x7)<<5);
}
}
}
void ReadBuffer256Fast(int x1, int y1, int x2, int y2, unsigned char *c){
int x,y,t;
uint8_t *pp, *qq=(uint8_t *)c;
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
int xx1=x1, yy1=y1, xx2=x2, yy2=y2;
if(x1 < 0) xx1 = 0;
if(x1 >= HRes) xx1 = HRes - 1;
if(x2 < 0) xx2 = 0;
if(x2 >= HRes) xx2 = HRes - 1;
if(y1 < 0) yy1 = 0;
if(y1 >= VRes) yy1 = VRes - 1;
if(y2 < 0) yy2 = 0;
if(y2 >= VRes) yy2 = VRes - 1;
for(y=yy1;y<=yy2;y++){
for(x=xx1;x<=xx2;x++){
pp=(uint8_t *)((uint32_t)(WriteBuf+y*HRes+x));
*qq++=*pp;
}
}
}
void ScrollLCD256(int lines){
if(lines==0)return;
if(lines >= 0) {
for(int i=0;i<VRes-lines;i++) {
int d=i*HRes,s=(i+lines)*HRes;
for(int c=0;c<(HRes);c++)WriteBuf[d+c]=WriteBuf[s+c];
}
DrawRectangle(0, VRes-lines, HRes - 1, VRes - 1, PromptBC); // erase the lines to be scrolled off
} else {
lines=-lines;
for(int i=VRes-1;i>=lines;i--) {
int d=i*HRes,s=(i-lines)*HRes;
for(int c=0;c<(HRes<<1);c++)WriteBuf[d+c]=WriteBuf[s+c];
}
DrawRectangle(0, 0, HRes - 1, lines - 1, PromptBC); // erase the lines introduced at the top
}
}
/* @endcond */
void cmd_tile(void){
unsigned char *tp;
uint32_t bcolour=0xFFFFFFFF,fcolour=0xFFFFFFFF;
int xlen=1,ylen=1;
if(DISPLAY_TYPE!=SCREENMODE1)error("Invalid for this screen mode");
if(checkstring(cmdline,(unsigned char *)"RESET")){
fcolour=(FullColour) ? RGB555(Option.DefaultFC): RGB332(Option.DefaultFC);
bcolour=(FullColour) ? RGB555(Option.DefaultBC): RGB332(Option.DefaultBC);
for(int x=0;x<X_TILE;x++){
for(int y=0;y<Y_TILE;y++){
#ifdef HDMI
if(FullColour){
#endif
if(fcolour!=0xFFFFFFFF) tilefcols[y*X_TILE+x]=fcolour;
if(bcolour!=0xFFFFFFFF) tilebcols[y*X_TILE+x]=bcolour;
#ifdef HDMI
} else {
if(fcolour!=0xFFFFFFFF) tilefcols_w[y*X_TILE+x]=fcolour;
if(bcolour!=0xFFFFFFFF) tilebcols_w[y*X_TILE+x]=bcolour;
}
#endif
}
}
} else if((tp=checkstring(cmdline,(unsigned char *)"HEIGHT"))){
ytileheight=getint(tp,8,VRes);
Y_TILE=VRes/ytileheight;
if(VRes % ytileheight)Y_TILE++;
ClearScreen(Option.DefaultBC);
} else {
getargs(&cmdline, 11, (unsigned char *)",");
if(!(DISPLAY_TYPE==SCREENMODE1))return;
if(argc<5)error("Syntax");
int x=getint(argv[0],0,X_TILE-1);
int y=getint(argv[2],0,Y_TILE-1);
int tilebcolour, tilefcolour ;
if(*argv[4]){
tilefcolour = getColour((char *)argv[4], 0);
fcolour = (FullColour) ? RGB555(tilefcolour): RGB332(tilefcolour);
}
if(argc>=7 && *argv[6]){
tilebcolour = getColour((char *)argv[6], 0);
bcolour = (FullColour) ? RGB555(tilebcolour): RGB332(tilebcolour);
}
if(argc>=9 && *argv[8]){
xlen=getint(argv[8],1,X_TILE-x);
}
if(argc>=11 && *argv[10]){
ylen=getint(argv[10],1,Y_TILE-y);
}
for(int xp=x;xp<x+xlen;xp++){
for(int yp=y;yp<y+ylen;yp++){
#ifdef HDMI
if(FullColour){
#endif
if(fcolour!=0xFFFFFFFF) tilefcols[yp*X_TILE+xp]=(uint16_t)fcolour;
if(bcolour!=0xFFFFFFFF) tilebcols[yp*X_TILE+xp]=(uint16_t)bcolour;
#ifdef HDMI
} else {
if(fcolour!=0xFFFFFFFF) tilefcols_w[yp*X_TILE+xp]=(uint8_t)fcolour;
if(bcolour!=0xFFFFFFFF) tilebcols_w[yp*X_TILE+xp]=(uint8_t)bcolour;
}
#endif
}
}
}
}
void cmd_map(void){
unsigned char *p;
static bool first=true;
if(!(DISPLAY_TYPE==SCREENMODE2 || DISPLAY_TYPE==SCREENMODE3 || DISPLAY_TYPE==SCREENMODE5 ))error("Invalid for this screen mode");
if((p=checkstring(cmdline, (unsigned char *)"RESET"))) {
while(v_scanline!=0){}
if(DISPLAY_TYPE==SCREENMODE5)for(int i=0;i<256;i++)map256[i]=remap[i]=RGB555(MAP256DEF[i]);
else if(FullColour)for(int i=0;i<16;i++){
map16[i]=remap[i]=RGB555(MAP16DEF[i]);
map16pairs[i]=map16[i] | (map16[i]<<16);
}
else if(DISPLAY_TYPE==SCREENMODE3)for(int i=0;i<16;i++) map16d[i]=remap[i]=RGB332(MAP16DEF[i]) | (RGB332(MAP16DEF[i])<<8);
else if(DISPLAY_TYPE==SCREENMODE2)for(int i=0;i<16;i++) map16q[i]=remap[i]=RGB332(MAP16DEF[i]) | (RGB332(MAP16DEF[i])<<8) | (RGB332(MAP16DEF[i])<<16) | (RGB332(MAP16DEF[i])<<24);
first=false;
} else if((checkstring(cmdline, (unsigned char *)"GRAYSCALE") || checkstring(cmdline, (unsigned char *)"GREYSCALE")) && (FullColour)) {
while(v_scanline!=0){}
if(DISPLAY_TYPE==SCREENMODE5) {
for(int i=1;i<=32;i++){
int j=i*8-(8-i/4+1);
if(j<0)j=0;
map256[i-1]=remap[i-1]=RGB555(j*65536 + j*256 + j);
map256[i+32-1]=remap[i+32-1]=RGB555(j);
map256[i+64-1]=remap[i+64-1]=RGB555(j*256 );
map256[i+96-1]=remap[i+96-1]=RGB555(j*256 + j);
map256[i+128-1]=remap[i+128-1]=RGB555(j*65536);
map256[i+160-1]=remap[i+160-1]=RGB555(j*65536 + j);
map256[i+192-1]=remap[i+192-1]=RGB555(j*65536 + j*256);
map256[i+224-1]=remap[i+224-1]=RGB555(j*65536 + j*256 + j);
}
} else {
for(int i=1;i<=16;i++){
int j=i*16-(16-i+1);
map16[i-1]=remap[i-1]= RGB555(j*65536+j*256+j);
map16pairs[i-1]=map16[i-1] | (map16[i-1]<<16);
}
}
first=false;
} else if((p=checkstring(cmdline, (unsigned char *)"MAXIMITE"))) {
while(v_scanline!=0){}
if(DISPLAY_TYPE==SCREENMODE5)for(int i=0;i<16;i++)map256[i]=remap[i]=RGB555(CMM1map[i]);
else for(int i=0;i<16;i++){
map16[i]=remap[i]=RGB555(CMM1map[i]);
map16pairs[i]=map16[i] | (map16[i]<<16);
#ifdef HDMI
if(DISPLAY_TYPE==SCREENMODE3 && (FullColour))map16d[i]=remap[i]=RGB332(CMM1map[i]) | (RGB332(CMM1map[i])<<8);
if(DISPLAY_TYPE==SCREENMODE2 && (FullColour))map16q[i]=remap[i]=RGB332(CMM1map[i]) | (RGB332(CMM1map[i])<<8)| (RGB332(CMM1map[i])<<16)| (RGB332(CMM1map[i])<<24);
#endif
}
first=false;
} else if((p=checkstring(cmdline, (unsigned char *)"SET"))) {
while(v_scanline!=0){}
if(DISPLAY_TYPE==SCREENMODE5) for(int i=0;i<256;i++)map256[i]=remap[i];
else for(int i=0;i<16;i++){
if(FullColour){map16[i]=remap[i];map16pairs[i]=map16[i] | (map16[i]<<16);}
else if(DISPLAY_TYPE==SCREENMODE3)map16d[i]=remap[i];
else if(DISPLAY_TYPE==SCREENMODE2)map16q[i]=remap[i];
}
} else {
int cl = getint(cmdline,0,255);
if(DISPLAY_TYPE!=SCREENMODE5 && cl >15)error("Mode supports 16 colours (0-15)");
while(*cmdline && tokenfunction(*cmdline) != op_equal) cmdline++;
if(!*cmdline) error("Invalid syntax");
++cmdline;
if(!*cmdline) error("Invalid syntax");
int col=getColour((char *)cmdline,0);
if(first){
if(DISPLAY_TYPE==SCREENMODE5)for(int i=0;i<256;i++)remap[i]=RGB555(MAP256DEF[i]);
else if(FullColour)for(int i=0;i<16;i++)remap[i]=RGB555(MAP16DEF[i]);
else if(DISPLAY_TYPE==SCREENMODE3)for(int i=0;i<16;i++)remap[i]=RGB332(MAP16DEF[i]) | (RGB332(MAP16DEF[i])<<8);
else if(DISPLAY_TYPE==SCREENMODE2)for(int i=0;i<16;i++)remap[i]=RGB332(MAP16DEF[i]) | (RGB332(MAP16DEF[i])<<8) | (RGB332(MAP16DEF[i])<<16) | (RGB332(MAP16DEF[i])<<24);
first=false;
}
if(FullColour)remap[cl]=RGB555(col);
else if(DISPLAY_TYPE==SCREENMODE3)remap[cl]=RGB332(col) | (RGB332(col)<<8);
else if(DISPLAY_TYPE==SCREENMODE2)remap[cl]=RGB332(col) | (RGB332(col)<<8) | (RGB332(col)<<16) | (RGB332(col)<<24);
}
}
#endif
void setmode(int mode, bool clear){
closeframebuffer('A');
if(clear)memset((void *)FRAMEBUFFER,0,framebuffersize);
if(mode==5){
DISPLAY_TYPE=SCREENMODE5;
ScreenSize=MODE5SIZE;
} else if(mode==4){
if(!(FullColour))error("Mode not available in this resolution");
DISPLAY_TYPE=SCREENMODE4;
ScreenSize=MODE4SIZE;
} else if(mode==3){
DISPLAY_TYPE=SCREENMODE3;
ScreenSize=MODE3SIZE;
} else if(mode==2){
DISPLAY_TYPE=SCREENMODE2;
ScreenSize=MODE2SIZE;
} else { //mode=1
#ifdef rp2350
#ifndef HDMI
tilefcols=(uint16_t *)((uint8_t*)FRAMEBUFFER+(MODE1SIZE*3));
tilebcols=(uint16_t *)((uint8_t*)FRAMEBUFFER+(MODE1SIZE*3)+(MODE1SIZE>>1));
#endif
#endif
DISPLAY_TYPE=SCREENMODE1;
ScreenSize=MODE1SIZE;
}
// uSec(10000);
ResetDisplay();
if(clear){
memset((void *)WriteBuf, 0, ScreenSize);
CurrentX = CurrentY =0;
ClearScreen(Option.DefaultBC);
}
#ifdef HDMI
if(FullColour || MediumRes){
#endif
if(DISPLAY_TYPE==SCREENMODE2 || DISPLAY_TYPE==SCREENMODE4 || DISPLAY_TYPE==SCREENMODE5){
SetFont((6<<4) | 1) ;
PromptFont=(6<<4) | 1;
} else {
SetFont(1) ;
PromptFont = 1;
}
#ifdef HDMI
} else {
if(DISPLAY_TYPE==SCREENMODE1){
SetFont((2<<4) | 1) ;
PromptFont=(2<<4) | 1;
} else if(DISPLAY_TYPE==SCREENMODE2 || DISPLAY_TYPE==SCREENMODE5){
SetFont((6<<4) | 1) ;
PromptFont=(6<<4) | 1;
} else if(DISPLAY_TYPE==SCREENMODE3){
SetFont(1) ;
PromptFont = 1;
}
}
#endif
if(DISPLAY_TYPE==SCREENMODE1){
ytileheight=gui_font_height;
Y_TILE=VRes/ytileheight;
if(VRes % ytileheight)Y_TILE++;
#ifdef PICOMITEVGA
if(DISPLAY_TYPE==SCREENMODE1/* && WriteBuf==DisplayBuf*/){
gui_fcolour=Option.DefaultFC;
gui_bcolour=Option.DefaultBC;
#ifdef HDMI
settiles();
#else
int bcolour = RGB121pack(gui_bcolour);
int fcolour = RGB121pack(gui_fcolour);
for(int x=0;x<X_TILE;x++){
for(int y=0;y<Y_TILE;y++){
tilefcols[y*X_TILE+x]=fcolour;
tilebcols[y*X_TILE+x]=bcolour;
}
}
#endif
}
#endif
}
#ifdef USBKEYBOARD
clearrepeat();
#endif
}
void cmd_mode(void){
int mode =getint(cmdline,1,MAXMODES);
setmode(mode, true);
}
#endif
/*
* @cond
* The following section will be excluded from the documentation.
*/
/* @endcond */
void fun_mmcharwidth(void) {
if(Option.DISPLAY_TYPE == 0) error("Display not configured");
iret = FontTable[gui_font >> 4][0] * (gui_font & 0b1111);
targ = T_INT;
}
void fun_mmcharheight(void) {
if(Option.DISPLAY_TYPE == 0) error("Display not configured");
iret = FontTable[gui_font >> 4][1] * (gui_font & 0b1111);
targ = T_INT;
}
/* @endcond */
/****************************************************************************************************
****************************************************************************************************
Basic drawing primitives for a user defined LCD display driver (ie, OPTION LCDPANEL USER)
all drawing is done using either DrawRectangleUser() or DrawBitmapUser()
****************************************************************************************************
****************************************************************************************************/
void cmd_refresh(void){
if(Option.DISPLAY_TYPE == 0) error("Display not configured");
low_y=0; high_y=DisplayVRes-1; low_x=0; high_x=DisplayHRes-1;
Display_Refresh();
}
/*
* @cond
* The following section will be excluded from the documentation.
*/
void DrawPixel16(int x, int y, int c){
if(x<0 || y<0 || x>=HRes || y>=VRes)return;
if((Option.DISPLAY_TYPE>=VIRTUAL && Option.DISPLAY_TYPE<NEXTGEN) && WriteBuf==NULL) WriteBuf=GetMemory(VMaxH*VMaxV/8);
unsigned char colour = RGB121(c);
uint8_t *p=(uint8_t *)(((uint32_t) WriteBuf)+(y*(HRes>>1))+(x>>1));
if(x & 1){
*p &=0x0F;
*p |=(colour<<4);
} else {
*p &=0xF0;
*p |= colour;
}
}
void DrawRectangle16(int x1, int y1, int x2, int y2, int c){
int x,y,x1p,x2p,t;
// unsigned char mask;
unsigned char colour = RGB121(c);;
unsigned char bcolour=(colour<<4) | colour;
if((Option.DISPLAY_TYPE>=VIRTUAL && Option.DISPLAY_TYPE<NEXTGEN) && WriteBuf==NULL) WriteBuf=GetMemory(VMaxH*VMaxV/8);
if(x1 < 0) x1 = 0;
if(x1 >= HRes) x1 = HRes - 1;
if(x2 < 0) x2 = 0;
if(x2 >= HRes) x2 = HRes - 1;
if(y1 < 0) y1 = 0;
if(y1 >= VRes) y1 = VRes - 1;
if(y2 < 0) y2 = 0;
if(y2 >= VRes) y2 = VRes - 1;
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
for(y=y1;y<=y2;y++){
x1p=x1;
x2p=x2;
uint8_t *p=(uint8_t *)(((uint32_t) WriteBuf)+(y*(HRes>>1))+(x1>>1));
if((x1 % 2) == 1){
*p &=0x0F;
*p |=(colour<<4);
p++;
x1p++;
}
if((x2 % 2) == 0){
uint8_t *q=(uint8_t *)(((uint32_t) WriteBuf)+(y*(HRes>>1))+(x2>>1));
*q &=0xF0;
*q |= colour;
x2p--;
}
for(x=x1p;x<x2p;x+=2){
*p++=bcolour;
}
}
}
void DrawBitmap16(int x1, int y1, int width, int height, int scale, int fc, int bc, unsigned char *bitmap){
int i, j, k, m, x, y;
// unsigned char mask;
if(x1>=HRes || y1>=VRes || x1+width*scale<0 || y1+height*scale<0)return;
unsigned char fcolour = RGB121(fc);
unsigned char bcolour = RGB121(bc);
if((Option.DISPLAY_TYPE>=VIRTUAL && Option.DISPLAY_TYPE<NEXTGEN) && WriteBuf==NULL) WriteBuf=GetMemory(VMaxH*VMaxV/8);
for(i = 0; i < height; i++) { // step thru the font scan line by line
for(j = 0; j < scale; j++) { // repeat lines to scale the font
for(k = 0; k < width; k++) { // step through each bit in a scan line
for(m = 0; m < scale; m++) { // repeat pixels to scale in the x axis
x=x1 + k * scale + m ;
y=y1 + i * scale + j ;
if(x >= 0 && x < HRes && y >= 0 && y < VRes) { // if the coordinates are valid
uint8_t *p=(uint8_t *)(((uint32_t) WriteBuf)+(y*(HRes>>1))+(x>>1));
if((bitmap[((i * width) + k)/8] >> (((height * width) - ((i * width) + k) - 1) %8)) & 1) {
if(x & 1){
*p &=0x0F;
*p |=(fcolour<<4);
} else {
*p &=0xF0;
*p |= fcolour;
}
} else {
if(bc>=0){
if(x & 1){
*p &=0x0F;
*p |=(bcolour<<4);
} else {
*p &=0xF0;
*p |= bcolour;
}
}
}
}
}
}
}
}
}
void ScrollLCD16(int lines){
if(lines==0)return;
if(lines >= 0) {
for(int i=0;i<VRes-lines;i++) {
int d=i*(HRes>>1),s=(i+lines)*(HRes>>1);
for(int c=0;c<(HRes>>1);c++)WriteBuf[d+c]=WriteBuf[s+c];
}
DrawRectangle(0, VRes-lines, HRes - 1, VRes - 1, PromptBC); // erase the lines to be scrolled off
} else {
lines=-lines;
for(int i=VRes-1;i>=lines;i--) {
int d=i*(HRes>>1),s=(i-lines)*(HRes>>1);
for(int c=0;c<(HRes>>1);c++)WriteBuf[d+c]=WriteBuf[s+c];
}
DrawRectangle(0, 0, HRes - 1, lines - 1, PromptBC); // erase the lines introduced at the top
}
}
void DrawBuffer16(int x1, int y1, int x2, int y2, unsigned char *p){
int x,y, t;
union colourmap
{
char rgbbytes[4];
unsigned int rgb;
} c;
unsigned char fcolour;
uint8_t *pp;
if((Option.DISPLAY_TYPE>=VIRTUAL && Option.DISPLAY_TYPE<NEXTGEN) && WriteBuf==NULL) WriteBuf=GetMemory(VMaxH*VMaxV/8);
// make sure the coordinates are kept within the display area
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
if(x1 < 0) x1 = 0;
if(x1 >= HRes) x1 = HRes - 1;
if(x2 < 0) x2 = 0;
if(x2 >= HRes) x2 = HRes - 1;
if(y1 < 0) y1 = 0;
if(y1 >= VRes) y1 = VRes - 1;
if(y2 < 0) y2 = 0;
if(y2 >= VRes) y2 = VRes - 1;
for(y=y1;y<=y2;y++){
for(x=x1;x<=x2;x++){
c.rgbbytes[0]=*p++; //this order swaps the bytes to match the .BMP file
c.rgbbytes[1]=*p++;
c.rgbbytes[2]=*p++;
fcolour = RGB121(c.rgb);
pp=(uint8_t *)(((uint32_t) WriteBuf)+(y*(HRes>>1))+(x>>1));
if(x & 1){
*pp &=0x0F;
*pp |=(fcolour<<4);
} else {
*pp &=0xF0;
*pp |= fcolour;
}
}
}
}
void DrawBuffer16Fast(int x1, int y1, int x2, int y2, int blank, unsigned char *p){
int x,y, t,toggle=0;
unsigned char c,w;
uint8_t *pp;
// make sure the coordinates are kept within the display area
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
if((Option.DISPLAY_TYPE>=VIRTUAL && Option.DISPLAY_TYPE<NEXTGEN) && WriteBuf==NULL) WriteBuf=GetMemory(VMaxH*VMaxV/8);
for(y=y1;y<=y2;y++){
for(x=x1;x<=x2;x++){
if(x>=0 && x<HRes && y>=0 && y<VRes){
pp=(uint8_t *)(WriteBuf+(y*(HRes>>1))+(x>>1));
if(x & 1){
w=*pp & 0xF0;
*pp &=0x0F;
if(toggle){
c=((*p++)&0xF0);
} else {
c=(*p<<4);
}
} else {
w=*pp & 0xF;
*pp &=0xF0;
if(toggle){
c = ((*p++)>>4);
} else {
c = (*p & 0xF);
}
}
if((!(c==sprite_transparent || c==sprite_transparent<<4)) || blank==-1)*pp |=c;
else *pp |=w;
toggle=!toggle;
} else {
if(toggle)p++;
toggle=!toggle;
}
}
}
}
void ReadBuffer16(int x1, int y1, int x2, int y2, unsigned char *c){
int x,y,t;
uint8_t *pp;
if((Option.DISPLAY_TYPE>=VIRTUAL && Option.DISPLAY_TYPE<NEXTGEN) && WriteBuf==NULL) WriteBuf=GetMemory(VMaxH*VMaxV/8);
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
int xx1=x1, yy1=y1, xx2=x2, yy2=y2;
if(x1 < 0) xx1 = 0;
if(x1 >= HRes) xx1 = HRes - 1;
if(x2 < 0) xx2 = 0;
if(x2 >= HRes) xx2 = HRes - 1;
if(y1 < 0) yy1 = 0;
if(y1 >= VRes) yy1 = VRes - 1;
if(y2 < 0) yy2 = 0;
if(y2 >= VRes) yy2 = VRes - 1;
for(y=yy1;y<=yy2;y++){
for(x=xx1;x<=xx2;x++){
pp=(uint8_t *)(((uint32_t) WriteBuf)+(y*(HRes>>1))+(x>>1));
#ifdef PICOMITEVGA
unsigned int q;
uint8_t *qq=pp;
if(WriteBuf==DisplayBuf && LayerBuf != DisplayBuf && LayerBuf !=NULL)qq=(uint8_t *)(((uint32_t) LayerBuf)+(y*(HRes>>1))+(x>>1));
#endif
if(x & 1){
t=colours[(*pp)>>4];
#ifdef PICOMITEVGA
q=colours[(*qq)>>4];
if(!(((*qq)>>4)==transparent) && mergedread)t=q;
#endif
} else {
t=colours[(*pp)&0x0F];
#ifdef PICOMITEVGA
q=colours[(*qq)&0x0F];
if(!(((*qq)&0x0F)==transparent) && mergedread)t=q;
#endif
}
*c++=(t&0xFF);
*c++=(t>>8)&0xFF;
*c++=t>>16;
}
}
}
void ReadBuffer16Fast(int x1, int y1, int x2, int y2, unsigned char *c){
int x,y,t,toggle=0;
uint8_t *pp;
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
if((Option.DISPLAY_TYPE>=VIRTUAL && Option.DISPLAY_TYPE<NEXTGEN) && WriteBuf==NULL) WriteBuf=GetMemory(VMaxH*VMaxV/8);
for(y=y1;y<=y2;y++){
for(x=x1;x<=x2;x++){
if(x>=0 && x<HRes && y>=0 && y<VRes){
pp=(uint8_t *)(((uint32_t) WriteBuf)+(y*(HRes>>1))+(x>>1));
if(!(x & 1)){
if(toggle)*c++ |= (((*pp)&0x0F))<<4;
else *c=((*pp)&0x0F);
} else {
if(toggle)*c++ |=((*pp)&0xF0);
else *c=((*pp)>>4);
}
toggle=!toggle;
} else {
if(toggle) *c++ &= 0xF;
else *c = 0 ;
toggle=!toggle;
}
}
}
}
#ifdef PICOMITEVGA
void Display_Refresh(void){
}
#endif
void DrawPixel2(int x, int y, int c){
if(x<0 || y<0 || x>=HRes || y>=VRes)return;
if((Option.DISPLAY_TYPE>=VIRTUAL && Option.DISPLAY_TYPE<NEXTGEN) && WriteBuf==NULL) WriteBuf=GetMemory(VMaxH*VMaxV/8);
uint8_t *p=(uint8_t *)(((uint32_t) WriteBuf)+(y*(HRes>>3))+(x>>3));
uint8_t bit = 1<<(x % 8);
if(c)*p |=bit;
else *p &= ~bit;
}
void DrawRectangle2(int x1, int y1, int x2, int y2, int c){
int x,y,x1p, x2p, t;
unsigned char mask;
volatile unsigned char *p;
if((Option.DISPLAY_TYPE>=VIRTUAL && Option.DISPLAY_TYPE<NEXTGEN) && WriteBuf==NULL) WriteBuf=GetMemory(VMaxH*VMaxV/8);
if(x1 < 0) x1 = 0;
if(x1 >= HRes) x1 = HRes - 1;
if(x2 < 0) x2 = 0;
if(x2 >= HRes) x2 = HRes - 1;
if(y1 < 0) y1 = 0;
if(y1 >= VRes) y1 = VRes - 1;
if(y2 < 0) y2 = 0;
if(y2 >= VRes) y2 = VRes - 1;
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
if(x1==x2){
for(y=y1;y<=y2;y++){
p=&WriteBuf[(y*(HRes>>3))+(x1>>3)];
mask=1<<(x1 % 8); //get the bit position for this bit
if(c){
*p|=mask;
} else {
*p&=(~mask);
}
}
} else {
for(y=y1;y<=y2;y++){
x1p=x1;
x2p=x2;
if((x1 % 8) !=0){
p=&WriteBuf[(y*(HRes>>3))+(x1>>3)];
for(x=x1;x<=x2 && (x % 8)!=0;x++){
mask=1<<(x % 8); //get the bit position for this bit
if(c){
*p|=mask;
} else {
*p&=(~mask);
}
x1p++;
}
}
if(x1p-1!=x2 && (x2 % 8)!=7){
p=&WriteBuf[(y*(HRes>>3))+(x2p>>3)];
for(x=(x2 & 0xFFF8);x<=x2 ;x++){
mask=1<<(x % 8); //get the bit position for this bit
if(c){
*p|=mask;
} else {
*p&=(~mask);
}
x2p--;
}
}
p=&WriteBuf[(y*(HRes>>3))+(x1p>>3)];
for(x=x1p;x<x2p;x+=8){
if(c){
*p++=0xFF;
} else {
*p++=0;
}
}
}
}
}
void DrawBitmap2(int x1, int y1, int width, int height, int scale, int fc, int bc, unsigned char *bitmap){
int i, j, k, m, x, y, loc;
unsigned char mask;
if(x1>=HRes || y1>=VRes || x1+width*scale<0 || y1+height*scale<0)return;
int tilematch=0;
if((Option.DISPLAY_TYPE>=VIRTUAL && Option.DISPLAY_TYPE<NEXTGEN) && WriteBuf==NULL) WriteBuf=GetMemory(VMaxH*VMaxV/8);
#ifdef PICOMITEVGA
int xa= 8;
int ya=ytileheight;
if(x1 % xa == 0 && y1 % ya==0 && width*scale % xa==0 && height*scale % ya==0)tilematch=1;
#endif
if(fc==0 && bc>0 && (tilematch==0 || editactive)){
for(i = 0; i < height; i++) { // step thru the font scan line by line
for(j = 0; j < scale; j++) { // repeat lines to scale the font
for(k = 0; k < width; k++) { // step through each bit in a scan line
for(m = 0; m < scale; m++) { // repeat pixels to scale in the x axis
x=x1 + k * scale + m ;
y=y1 + i * scale + j ;
mask=1<<(x % 8); //get the bit position for this bit
if(x >= 0 && x < HRes && y >= 0 && y < VRes) { // if the coordinates are valid
loc=(y*(HRes>>3))+(x>>3);
if((bitmap[((i * width) + k)/8] >> (((height * width) - ((i * width) + k) - 1) %8)) & 1) {
WriteBuf[loc]&= ~mask;
} else WriteBuf[loc]|= mask;
}
}
}
}
}
} else {
#ifdef PICOMITEVGA
if(tilematch){
// the bitmap is aligned with the tiles
int bcolour, fcolour ;
#ifdef HDMI
fcolour = (FullColour)? RGB555(fc) : RGB332(fc);
bcolour = (FullColour)? RGB555(bc) : RGB332(bc);
#else
fcolour = RGB121pack(fc);
bcolour = RGB121pack(bc);
#endif
int xt=x1 / xa;
int yt=y1 / ya;
int w=width*scale/xa;
int h=height*scale/ya;
// int pos;
#ifdef HDMI
if(FullColour){
#endif
for(int yy=yt;yy<yt+h;yy++){
for(int xx=xt; xx<xt+w;xx++){
tilefcols[yy*X_TILE+xx]=(uint16_t)fcolour;
tilebcols[yy*X_TILE+xx]=(uint16_t)bcolour;
}
}
#ifdef HDMI
} else {
for(int yy=yt;yy<yt+h;yy++){
for(int xx=xt; xx<xt+w;xx++){
tilefcols_w[yy*X_TILE+xx]=(uint8_t)fcolour;
tilebcols_w[yy*X_TILE+xx]=(uint8_t)bcolour;
}
}
}
#endif
}
#endif
if(fc==0 && bc!=0 && fc!=bc && bc!=-1) fc=1;
if(bc<=0 || fc==0){
for(i = 0; i < height; i++) { // step thru the font scan line by line
for(j = 0; j < scale; j++) { // repeat lines to scale the font
for(k = 0; k < width; k++) { // step through each bit in a scan line
for(m = 0; m < scale; m++) { // repeat pixels to scale in the x axis
x=x1 + k * scale + m ;
y=y1 + i * scale + j ;
mask=1<<(x % 8); //get the bit position for this bit
if(x >= 0 && x < HRes && y >= 0 && y < VRes) { // if the coordinates are valid
loc=(y*(HRes>>3))+(x>>3);
if((bitmap[((i * width) + k)/8] >> (((height * width) - ((i * width) + k) - 1) %8)) & 1) {
if(fc){
WriteBuf[loc]|=mask;
} else {
WriteBuf[loc]&= ~mask;
}
} else {
if(bc>0){
WriteBuf[loc]|=mask;
} else if(bc==0) {
WriteBuf[loc]&= ~mask;
}
}
}
}
}
}
}
} else {
for(i = 0; i < height; i++) { // step thru the font scan line by line
for(j = 0; j < scale; j++) { // repeat lines to scale the font
for(k = 0; k < width; k++) { // step through each bit in a scan line
for(m = 0; m < scale; m++) { // repeat pixels to scale in the x axis
x=x1 + k * scale + m ;
y=y1 + i * scale + j ;
mask=1<<(x % 8); //get the bit position for this bit
if(x >= 0 && x < HRes && y >= 0 && y < VRes) { // if the coordinates are valid
loc=(y*(HRes>>3))+(x>>3);
if((bitmap[((i * width) + k)/8] >> (((height * width) - ((i * width) + k) - 1) %8)) & 1) {
if(fc){
WriteBuf[loc]|=mask;
} else {
WriteBuf[loc]&= ~mask;
}
} else WriteBuf[loc]&= ~mask;
}
}
}
}
}
}
}
}
void ScrollLCD2(int lines){
if(lines==0)return;
if(lines >= 0) {
#ifdef PICOMITEVGA
#ifndef HDMI
while(QVgaScanLine!=0){}
#else
while(v_scanline!=0){}
#endif
int ya=ytileheight;
if((lines % ya ==0)){
int offset=lines/ya;
for(int y=0;y<Y_TILE-offset;y++){
int d=y*X_TILE;
int s=(y+offset)*X_TILE;
for(int x=0;x<X_TILE;x++){
#ifdef HDMI
if(FullColour){
#endif
tilefcols[d+x]=tilefcols[s+x];
tilebcols[d+x]=tilebcols[s+x];
#ifdef HDMI
} else {
tilefcols_w[d+x]=tilefcols_w[s+x];
tilebcols_w[d+x]=tilebcols_w[s+x];
}
#endif
}
}
}
#endif
for(int i=0;i<VRes-lines;i++) {
int d=i*(HRes>>3),s=(i+lines)*(HRes>>3);
for(int c=0;c<(HRes>>3);c++)WriteBuf[d+c]=WriteBuf[s+c];
}
DrawRectangle(0, VRes-lines, HRes - 1, VRes - 1, 0); // erase the lines to be scrolled off
} else {
lines=-lines;
#ifdef PICOMITEVGA
#ifndef HDMI
while(QVgaScanLine!=0){}
#else
while(v_scanline!=0){}
#endif
int ya=ytileheight;
if((lines % ya ==0)){
int offset=lines/ya;
for(int y=Y_TILE-1;y>=offset;y--){
int d=y*X_TILE;
int s=(y-offset)*X_TILE;
for(int x=0;x<X_TILE;x++){
#ifdef HDMI
if(FullColour){
#endif
tilefcols[d+x]=tilefcols[s+x];
tilebcols[d+x]=tilebcols[s+x];
#ifdef HDMI
} else {
tilefcols_w[d+x]=tilefcols_w[s+x];
tilebcols_w[d+x]=tilebcols_w[s+x];
}
#endif
}
}
}
#endif
for(int i=VRes-1;i>=lines;i--) {
int d=i*(HRes>>3),s=(i-lines)*(HRes>>3);
for(int c=0;c<(HRes>>3);c++)WriteBuf[d+c]=WriteBuf[s+c];
}
DrawRectangle(0, 0, HRes - 1, lines - 1, 0); // erase the lines introduced at the top
}
}
void DrawBuffer2(int x1, int y1, int x2, int y2, unsigned char *p){
int x,y, t, loc;
unsigned char mask;
union colourmap
{
char rgbbytes[4];
unsigned int rgb;
} c;
if((Option.DISPLAY_TYPE>=VIRTUAL && Option.DISPLAY_TYPE<NEXTGEN) && WriteBuf==NULL) WriteBuf=GetMemory(VMaxH*VMaxV/8);
// make sure the coordinates are kept within the display area
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
if(x1 < 0) x1 = 0;
if(x1 >= HRes) x1 = HRes - 1;
if(x2 < 0) x2 = 0;
if(x2 >= HRes) x2 = HRes - 1;
if(y1 < 0) y1 = 0;
if(y1 >= VRes) y1 = VRes - 1;
if(y2 < 0) y2 = 0;
if(y2 >= VRes) y2 = VRes - 1;
for(y=y1;y<=y2;y++){
for(x=x1;x<=x2;x++){
c.rgbbytes[0]=*p++;
if(c.rgbbytes[0]<0x40)c.rgbbytes[0]=0;
c.rgbbytes[1]=*p++;
if(c.rgbbytes[1]<0x40)c.rgbbytes[1]=0;
c.rgbbytes[2]=*p++;
if(c.rgbbytes[2]<0x40)c.rgbbytes[2]=0;
c.rgbbytes[3]=0;
loc=(y*(HRes>>3))+(x>>3);
mask=1<<(x % 8); //get the bit position for this bit
if(c.rgb){
WriteBuf[loc]|=mask;
} else {
WriteBuf[loc]&=(~mask);
}
}
}
}
void DrawBuffer2Fast(int x1, int y1, int x2, int y2, int blank, unsigned char *p){
int x,y, t, loc, toggle=0;
unsigned char mask;
// make sure the coordinates are kept within the display area
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
if((Option.DISPLAY_TYPE>=VIRTUAL && Option.DISPLAY_TYPE<NEXTGEN) && WriteBuf==NULL) WriteBuf=GetMemory(VMaxH*VMaxV/8);
for(y=y1;y<=y2;y++){
for(x=x1;x<=x2;x++){
if(x>=0 && x<HRes && y>=0 && y<VRes){
loc=(y*(HRes>>3))+(x>>3);
mask=1<<(x % 8); //get the bit position for this bit
if(toggle){
if(*p++ & 0xF0){
WriteBuf[loc]|=mask;
} else if(blank==-1){
WriteBuf[loc]&=(~mask);
}
} else {
if(*p & 0xF){
WriteBuf[loc]|=mask;
} else if(blank==-1){
WriteBuf[loc]&=(~mask);
}
}
toggle=!toggle;
} else {
if(toggle)p++;
toggle=!toggle;
}
}
}
}
void ReadBuffer2(int x1, int y1, int x2, int y2, unsigned char *c){
int x,y,t,loc;
// uint8_t *pp;
unsigned char mask;
if((Option.DISPLAY_TYPE>=VIRTUAL && Option.DISPLAY_TYPE<NEXTGEN) && WriteBuf==NULL) WriteBuf=GetMemory(VMaxH*VMaxV/8);
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
int xx1=x1, yy1=y1, xx2=x2, yy2=y2;
if(x1 < 0) xx1 = 0;
if(x1 >= HRes) xx1 = HRes - 1;
if(x2 < 0) xx2 = 0;
if(x2 >= HRes) xx2 = HRes - 1;
if(y1 < 0) yy1 = 0;
if(y1 >= VRes) yy1 = VRes - 1;
if(y2 < 0) yy2 = 0;
if(y2 >= VRes) yy2 = VRes - 1;
for(y=yy1;y<=yy2;y++){
for(x=xx1;x<=xx2;x++){
#ifdef PICOMITEVGA
int tile= x/8 + (y/ytileheight)*X_TILE;
int back=RGB121map[tilebcols[tile] & 0xF];
int front=RGB121map[tilefcols[tile] & 0xF];
#else
int front=0xFFFFFF;
int back=0;
#endif
loc=(y*(HRes>>3))+(x>>3);
mask=1<<(x % 8); //get the bit position for this bit
if(WriteBuf[loc]&mask){
*c++=(front&0xFF);
*c++=(front>>8)&0xFF;
*c++=front>>16;
} else {
*c++=(back&0xFF);
*c++=(back>>8)&0xFF;
*c++=back>>16;
}
}
}
}
void ReadBuffer2Fast(int x1, int y1, int x2, int y2, unsigned char *c){
int x,y,t,loc,toggle=0;;
// uint8_t *pp;
unsigned char mask;
if((Option.DISPLAY_TYPE>=VIRTUAL && Option.DISPLAY_TYPE<NEXTGEN) && WriteBuf==NULL) WriteBuf=GetMemory(VMaxH*VMaxV/8);
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
for(y=y1;y<=y2;y++){
for(x=x1;x<=x2;x++){
if(x>=0 && x<HRes && y>=0 && y<VRes){
loc=(y*(HRes>>3))+(x>>3);
mask=1<<(x % 8); //get the bit position for this bit
if(toggle){
if(WriteBuf[loc]&mask){
*c++|=0xF0;
} else {
*c++&=0x0F;
}
} else {
if(WriteBuf[loc]&mask){
*c=0xF;
} else {
*c=0x0;
}
}
toggle=!toggle;
} else {
if(toggle) *c++ &= 0xF;
else *c = 0 ;
toggle=!toggle;
}
}
}
}
void MIPS16 ConfigDisplayVirtual(unsigned char *p) {
getargs(&p, 13, (unsigned char *)",");
if(checkstring(argv[0], (unsigned char *)"VIRTUAL_M")) {
DISPLAY_TYPE = VIRTUAL_M;
} else if(checkstring(argv[0], (unsigned char *)"VIRTUAL_C")) {
DISPLAY_TYPE = VIRTUAL_C;
} else return;
Option.DISPLAY_TYPE=DISPLAY_TYPE;
Option.DISPLAY_ORIENTATION = LANDSCAPE;
}
void MIPS16 InitDisplayVirtual(void){
if(Option.DISPLAY_TYPE==0 || Option.DISPLAY_TYPE < VIRTUAL || Option.DISPLAY_TYPE >= NEXTGEN) return;
DisplayHRes = HRes = display_details[Option.DISPLAY_TYPE].horizontal;
DisplayVRes = VRes = display_details[Option.DISPLAY_TYPE].vertical;
if(Option.DISPLAY_TYPE==VIRTUAL_M){
DrawRectangle=DrawRectangle2;
DrawBitmap= DrawBitmap2;
ScrollLCD=ScrollLCD2;
DrawBuffer=DrawBuffer2;
ReadBuffer=ReadBuffer2;
DrawBufferFast=DrawBuffer2Fast;
ReadBufferFast=ReadBuffer2Fast;
DrawPixel=DrawPixel2;
} else {
DrawRectangle=DrawRectangle16;
DrawBitmap= DrawBitmap16;
ScrollLCD=ScrollLCD16;
DrawBuffer=DrawBuffer16;
ReadBuffer=ReadBuffer16;
DrawBufferFast=DrawBuffer16Fast;
ReadBufferFast=ReadBuffer16Fast;
DrawPixel=DrawPixel16;
}
if(WriteBuf==NULL)WriteBuf=GetMemory(VMaxH*VMaxV/8);
}
/* @endcond */
#ifdef PICOMITEVGA
#ifdef HDMI
void fun_getscanline(void){
if(Option.CPU_Speed==Freq720P){
iret = v_scanline - 30;
if(iret<0)iret+=750;
targ=T_INT;
} else if(Option.CPU_Speed==Freq480P){
iret = v_scanline - 20;
if(iret<0)iret+=500;
targ=T_INT;
} else if(Option.CPU_Speed==FreqXGA){
iret = v_scanline - 38;
if(iret<0)iret+=806;
targ=T_INT;
} else if(Option.CPU_Speed==FreqSVGA){
iret = v_scanline - 25;
if(iret<0)iret+=625;
targ=T_INT;
} else if(Option.CPU_Speed==Freq252P || Option.CPU_Speed==Freq378P){
iret = v_scanline - 45;
if(iret<0)iret+=525;
targ=T_INT;
} else if(Option.CPU_Speed==Freq848){
iret = v_scanline - 37;
if(iret<0)iret+=517;
targ=T_INT;
}
}
#else
void fun_getscanline(void){
iret=QVgaScanLine;
targ=T_INT;
}
#endif
#else
/*
* @cond
* The following section will be excluded from the documentation.
*/
// Draw a filled rectangle
// this is the basic drawing primitive used by most drawing routines
// x1, y1, x2, y2 - the coordinates
// c - the colour
void MIPS16 DrawRectangleUser(int x1, int y1, int x2, int y2, int c){
char callstr[256];
unsigned char *nextstmtSaved = nextstmt;
if(FindSubFun((unsigned char *)"MM.USER_RECTANGLE", 0) >= 0) {
strcpy(callstr, "MM.USER_RECTANGLE");
strcat(callstr, " "); IntToStr(callstr + strlen(callstr), x1, 10);
strcat(callstr, ","); IntToStr(callstr + strlen(callstr), y1, 10);
strcat(callstr, ","); IntToStr(callstr + strlen(callstr), x2, 10);
strcat(callstr, ","); IntToStr(callstr + strlen(callstr), y2, 10);
strcat(callstr, ","); IntToStr(callstr + strlen(callstr), c, 10);
callstr[strlen(callstr)+1] = 0; // two NULL chars required to terminate the call
g_LocalIndex++;
ExecuteProgram((unsigned char *)callstr);
nextstmt = nextstmtSaved;
g_LocalIndex--;
g_TempMemoryIsChanged = true; // signal that temporary memory should be checked
} else
error("MM.USER_RECTANGLE not defined");
}
//Print the bitmap of a char on the video output
// x, y - the top left of the char
// width, height - size of the char's bitmap
// scale - how much to scale the bitmap
// fc, bc - foreground and background colour
// bitmap - pointer to the bitmap
void MIPS16 DrawBitmapUser(int x1, int y1, int width, int height, int scale, int fc, int bc, unsigned char *bitmap){
char callstr[256];
unsigned char *nextstmtSaved = nextstmt;
if(FindSubFun((unsigned char *)"MM.USER_BITMAP", 0) >= 0) {
strcpy(callstr, "MM.USER_BITMAP");
strcat(callstr, " "); IntToStr(callstr + strlen(callstr), x1, 10);
strcat(callstr, ","); IntToStr(callstr + strlen(callstr), y1, 10);
strcat(callstr, ","); IntToStr(callstr + strlen(callstr), width, 10);
strcat(callstr, ","); IntToStr(callstr + strlen(callstr), height, 10);
strcat(callstr, ","); IntToStr(callstr + strlen(callstr), scale, 10);
strcat(callstr, ","); IntToStr(callstr + strlen(callstr), fc, 10);
strcat(callstr, ","); IntToStr(callstr + strlen(callstr), bc, 10);
strcat(callstr, ",&H"); IntToStr(callstr + strlen(callstr), (unsigned int)bitmap, 16);
callstr[strlen(callstr)+1] = 0; // two NULL chars required to terminate the call
g_LocalIndex++;
ExecuteProgram((unsigned char *)callstr);
g_LocalIndex--;
g_TempMemoryIsChanged = true; // signal that temporary memory should be checked
nextstmt = nextstmtSaved;
} else
error("MM.USER_BITMAP not defined");
}
#endif
/****************************************************************************************************
General purpose routines
****************************************************************************************************/
int GetFontWidth(int fnt) {
return FontTable[fnt >> 4][0] * (fnt & 0b1111);
}
int GetFontHeight(int fnt) {
return FontTable[fnt >> 4][1] * (fnt & 0b1111);
}
void SetFont(int fnt) {
if(FontTable[fnt >> 4] == NULL) error("Invalid font number #%", (fnt >> 4)+1);
gui_font_width = FontTable[fnt >> 4][0] * (fnt & 0b1111);
gui_font_height = FontTable[fnt >> 4][1] * (fnt & 0b1111);
if(Option.DISPLAY_CONSOLE) {
Option.Height = VRes/gui_font_height;
Option.Width = HRes/gui_font_width;
}
gui_font = fnt;
}
void MIPS16 ResetDisplay(void) {
SetFont(Option.DefaultFont);
gui_fcolour = Option.DefaultFC;
gui_bcolour = Option.DefaultBC;
PromptFont = Option.DefaultFont;
PromptFC = Option.DefaultFC;
PromptBC = Option.DefaultBC;
#ifdef PICOMITEVGA
#ifdef rp2350
if(Option.CPU_Speed==Freq848)HRes=((DISPLAY_TYPE == SCREENMODE1 || DISPLAY_TYPE == SCREENMODE3) ? 848: 424);
else if(Option.CPU_Speed==Freq400)HRes=((DISPLAY_TYPE == SCREENMODE1 || DISPLAY_TYPE == SCREENMODE3) ? 720: 360);
else if(Option.CPU_Speed==FreqSVGA)HRes=((DISPLAY_TYPE == SCREENMODE1 || DISPLAY_TYPE == SCREENMODE3) ? 800: 400);
else HRes=((DISPLAY_TYPE == SCREENMODE1 || DISPLAY_TYPE == SCREENMODE3) ? 640: 320);
#else
if(Option.CPU_Speed==Freq400)HRes=((DISPLAY_TYPE == SCREENMODE1 || DISPLAY_TYPE == SCREENMODE3) ? 720: 360);
else HRes=((DISPLAY_TYPE == SCREENMODE1 || DISPLAY_TYPE == SCREENMODE3) ? 640: 320);
#endif
if(Option.CPU_Speed==Freq400)VRes=((DISPLAY_TYPE == SCREENMODE1 || DISPLAY_TYPE == SCREENMODE3) ? 400: 200);
#ifdef rp2350
else if(Option.CPU_Speed==FreqSVGA)VRes=((DISPLAY_TYPE == SCREENMODE1 || DISPLAY_TYPE == SCREENMODE3) ? 600: 300);
#endif
else VRes=((DISPLAY_TYPE == SCREENMODE1 || DISPLAY_TYPE == SCREENMODE3) ? 480: 240);
#ifdef HDMI
if(Option.CPU_Speed==Freq720P){
HRes=(DISPLAY_TYPE == SCREENMODE1 ? 1280 : ((DISPLAY_TYPE==SCREENMODE2 || DISPLAY_TYPE==SCREENMODE5) ? 320 : 640));
VRes=(DISPLAY_TYPE == SCREENMODE1 ? 720 : ((DISPLAY_TYPE==SCREENMODE2 || DISPLAY_TYPE==SCREENMODE5) ? 180 : 360));
} else if(Option.CPU_Speed==FreqXGA){
HRes=(DISPLAY_TYPE == SCREENMODE1 ? 1024 : ((DISPLAY_TYPE==SCREENMODE2 || DISPLAY_TYPE==SCREENMODE5) ? 256 : 512));
VRes=(DISPLAY_TYPE == SCREENMODE1 ? 768 : ((DISPLAY_TYPE==SCREENMODE2 || DISPLAY_TYPE==SCREENMODE5) ? 192 : 384));
} else if(Option.CPU_Speed==FreqSVGA){
HRes=((DISPLAY_TYPE == SCREENMODE1 || DISPLAY_TYPE == SCREENMODE3) ? 800: 400);
VRes=((DISPLAY_TYPE == SCREENMODE1 || DISPLAY_TYPE == SCREENMODE3) ? 600: 300);
}
#endif
switch(DISPLAY_TYPE){
case SCREENMODE1:
ScreenSize= MODE1SIZE;
break;
case SCREENMODE2:
ScreenSize=MODE2SIZE;
break;
case SCREENMODE3:
ScreenSize=MODE3SIZE;
break;
case SCREENMODE4:
ScreenSize=MODE4SIZE;
break;
case SCREENMODE5:
ScreenSize=MODE5SIZE;
break;
}
if(DISPLAY_TYPE == SCREENMODE2 || DISPLAY_TYPE == SCREENMODE3){
DrawRectangle=DrawRectangle16;
DrawBitmap= DrawBitmap16;
ScrollLCD=ScrollLCD16;
DrawBuffer=DrawBuffer16;
ReadBuffer=ReadBuffer16;
DrawBufferFast=DrawBuffer16Fast;
ReadBufferFast=ReadBuffer16Fast;
DrawPixel=DrawPixel16;
#ifdef HDMI
} else if(DISPLAY_TYPE == SCREENMODE4){
DrawRectangle=DrawRectangle555;
DrawBitmap= DrawBitmap555;
ScrollLCD=ScrollLCD555;
DrawBuffer=DrawBuffer555;
ReadBuffer=ReadBuffer555;
DrawBufferFast=DrawBuffer555Fast;
ReadBufferFast=ReadBuffer555Fast;
DrawPixel=DrawPixel555;
} else if(DISPLAY_TYPE == SCREENMODE5){
DrawRectangle=DrawRectangle256;
DrawBitmap= DrawBitmap256;
ScrollLCD=ScrollLCD256;
DrawBuffer=DrawBuffer256;
ReadBuffer=ReadBuffer256;
DrawBufferFast=DrawBuffer256Fast;
ReadBufferFast=ReadBuffer256Fast;
DrawPixel=DrawPixel256;
#endif
} else {
DrawRectangle=DrawRectangle2;
DrawBitmap= DrawBitmap2;
ScrollLCD=ScrollLCD2;
DrawBuffer=DrawBuffer2;
ReadBuffer=ReadBuffer2;
DrawBufferFast=DrawBuffer2Fast;
ReadBufferFast=ReadBuffer2Fast;
DrawPixel=DrawPixel2;
PromptFC = gui_fcolour= Option.DefaultFC;
PromptBC = gui_bcolour= Option.DefaultBC;
}
#ifdef HDMI
settiles();
#else
#ifdef rp2350
if(DISPLAY_TYPE==SCREENMODE1){
tilefcols=(uint16_t *)((uint32_t)FRAMEBUFFER+(MODE1SIZE*3));
tilebcols=(uint16_t *)((uint32_t)FRAMEBUFFER+(MODE1SIZE*3)+(MODE1SIZE>>1));
}
#endif
for(int x=0;x<X_TILE;x++){
for(int y=0;y<Y_TILE;y++){
tilefcols[y*X_TILE+x]=RGB121pack(Option.DefaultFC);
tilebcols[y*X_TILE+x]=RGB121pack(Option.DefaultBC);
}
}
#endif
#else
#ifdef GUICONTROLS
ResetGUI();
#endif
#endif
}
void hline(int x0, int x1, int y, int f, int ints_per_line, uint32_t *br) { //draw a horizontal line
uint32_t w1, xx1, w0, xx0, x, xn, i;
const uint32_t a[]={0xFFFFFFFF,0x7FFFFFFF,0x3FFFFFFF,0x1FFFFFFF,0xFFFFFFF,0x7FFFFFF,0x3FFFFFF,0x1FFFFFF,
0xFFFFFF,0x7FFFFF,0x3FFFFF,0x1FFFFF,0xFFFFF,0x7FFFF,0x3FFFF,0x1FFFF,
0xFFFF,0x7FFF,0x3FFF,0x1FFF,0xFFF,0x7FF,0x3FF,0x1FF,
0xFF,0x7F,0x3F,0x1F,0x0F,0x07,0x03,0x01};
const uint32_t b[]={0x80000000,0xC0000000,0xe0000000,0xf0000000,0xf8000000,0xfc000000,0xfe000000,0xff000000,
0xff800000,0xffC00000,0xffe00000,0xfff00000,0xfff80000,0xfffc0000,0xfffe0000,0xffff0000,
0xffff8000,0xffffC000,0xffffe000,0xfffff000,0xfffff800,0xfffffc00,0xfffffe00,0xffffff00,
0xffffff80,0xffffffC0,0xffffffe0,0xfffffff0,0xfffffff8,0xfffffffc,0xfffffffe,0xffffffff};
w0 = y * (ints_per_line);
xx0 = 0;
w1 = y * (ints_per_line) + x1/32;
xx1 = (x1 & 0x1F);
w0 = y * (ints_per_line) + x0/32;
xx0 = (x0 & 0x1F);
w1 = y * (ints_per_line) + x1/32;
xx1 = (x1 & 0x1F);
if(w1==w0){ //special case both inside same word
x=(a[xx0] & b[xx1]);
xn=~x;
if(f)br[w0] |= x; else br[w0] &= xn; // turn on the pixel
} else {
if(w1-w0>1){ //first deal with full words
for(i=w0+1;i<w1;i++){
// draw the pixel
br[i]=0;
if(f)br[i] = 0xFFFFFFFF; // turn on the pixels
}
}
x=~a[xx0];
br[w0] &= x;
x=~x;
if(f)br[w0] |= x; // turn on the pixel
x=~b[xx1];
br[w1] &= x;
x=~x;
if(f)br[w1] |= x; // turn on the pixel
}
}
void DrawFilledCircle(int x, int y, int radius, int r, int fill, int ints_per_line, uint32_t *br, MMFLOAT aspect, MMFLOAT aspect2) {
int a, b, P;
int A, B, asp;
x=(int)((MMFLOAT)r*aspect)+radius;
y=r+radius;
a = 0;
b = radius;
P = 1 - radius;
asp = aspect2 * (MMFLOAT)(1 << 10);
do {
A = (a * asp) >> 10;
B = (b * asp) >> 10;
hline(x-A-radius, x+A-radius, y+b-radius, fill, ints_per_line, br);
hline(x-A-radius, x+A-radius, y-b-radius, fill, ints_per_line, br);
hline(x-B-radius, x+B-radius, y+a-radius, fill, ints_per_line, br);
hline(x-B-radius, x+B-radius, y-a-radius, fill, ints_per_line, br);
if(P < 0)
P+= 3 + 2*a++;
else
P+= 5 + 2*(a++ - b--);
} while(a <= b);
}
/******************************************************************************************
Print a char on the LCD display (SSD1963 and in landscape only). It handles control chars
such as newline and will wrap at the end of the line and scroll the display if necessary.
The char is printed at the current location defined by CurrentX and CurrentY
*****************************************************************************************/
void DisplayPutC(char c) {
if(!Option.DISPLAY_CONSOLE) return;
// if it is printable and it is going to take us off the right hand end of the screen do a CRLF
if(c >= FontTable[gui_font >> 4][2] && c < FontTable[gui_font >> 4][2] + FontTable[gui_font >> 4][3]) {
if(CurrentX + gui_font_width > HRes) {
DisplayPutC('\r');
DisplayPutC('\n');
}
}
// handle the standard control chars
switch(c) {
case '\b': CurrentX -= gui_font_width;
// if (CurrentX < 0) CurrentX = 0;
if(CurrentX < 0){ //Go to end of previous line
CurrentY -= gui_font_height ; //Go up one line
if (CurrentY < 0) CurrentY = 0;
CurrentX = (Option.Width-1) * gui_font_width; //go to last character
}
return;
case '\r': CurrentX = 0;
return;
case '\n':
if(CurrentY + 2* gui_font_height > VRes) {
if(Option.NoScroll && Option.DISPLAY_CONSOLE){ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;}
else {
ScrollLCD( gui_font_height);
}
} else {
CurrentY += gui_font_height;
}
return;
case '\t': do {
DisplayPutC(' ');
} while((CurrentX/gui_font_width) % Option.Tab);
return;
}
GUIPrintChar(gui_font, gui_fcolour, gui_bcolour, c, ORIENT_NORMAL); // print it
routinechecks();
}
void ShowCursor(int show) {
static int visible = false;
int newstate;
if(!Option.DISPLAY_CONSOLE) return;
newstate = ((CursorTimer <= CURSOR_ON) && show); // what should be the state of the cursor?
if(visible == newstate) return; // we can skip the rest if the cursor is already in the correct state
visible = newstate; // draw the cursor BELOW the font
DrawLine(CurrentX, CurrentY + gui_font_height-(gui_font_height<=12? 1:2), CurrentX + gui_font_width-1, CurrentY + gui_font_height-(gui_font_height<=12? 1:2), (gui_font_height<=12 ? 1 : 2), visible ? gui_fcolour : (DISPLAY_TYPE==SCREENMODE1 ? 0 :gui_bcolour));
}
#ifndef PICOMITEWEB
#define ABS(X) ((X)>0 ? (X) : (-(X)))
void DrawPolygon(int n, short *xcoord, short *ycoord, int face){
int i, facecount=struct3d[n]->facecount[face];
int c=struct3d[n]->line[face];
int f=struct3d[n]->fill[face];
// first deal with outline only
if(struct3d[n]->fill[face]==0xFFFFFFFF){
for(i=0;i<facecount;i++){
if(i<facecount-1){
DrawLine(xcoord[i],ycoord[i],xcoord[i+1],ycoord[i+1],1,c);
} else {
DrawLine(xcoord[i],ycoord[i],xcoord[0],ycoord[0],1,c);
}
}
} else {
if(facecount==3){
DrawTriangle(xcoord[0],ycoord[0],xcoord[1],ycoord[1],xcoord[2],ycoord[2],c,f);
} else if(facecount==4){
DrawTriangle(xcoord[0],ycoord[0],xcoord[1],ycoord[1],xcoord[2],ycoord[2],f,f);
DrawTriangle(xcoord[0],ycoord[0],xcoord[2],ycoord[2],xcoord[3],ycoord[3],f,f);
if(f!=c){
DrawLine(xcoord[0],ycoord[0],xcoord[1],ycoord[1],1,c);
DrawLine(xcoord[1],ycoord[1],xcoord[2],ycoord[2],1,c);
DrawLine(xcoord[2],ycoord[2],xcoord[3],ycoord[3],1,c);
DrawLine(xcoord[0],ycoord[0],xcoord[3],ycoord[3],1,c);
}
} else {
int ymax=-1000000, ymin=1000000;
fill_set_pen_color((c>>16) & 0xFF, (c>>8) & 0xFF , c & 0xFF);
fill_set_fill_color((f>>16) & 0xFF, (f>>8) & 0xFF , f & 0xFF);
fill_begin_fill();
main_fill_poly_vertex_count=0;
for(i=0;i<facecount;i++){
main_fill_polyX[main_fill_poly_vertex_count] = (TFLOAT)xcoord[i];
main_fill_polyY[main_fill_poly_vertex_count] = (TFLOAT)ycoord[i];
if(main_fill_polyY[main_fill_poly_vertex_count]>ymax)ymax=main_fill_polyY[main_fill_poly_vertex_count];
if(main_fill_polyY[main_fill_poly_vertex_count]<ymin)ymin=main_fill_polyY[main_fill_poly_vertex_count];
main_fill_polyX[main_fill_poly_vertex_count] = (TFLOAT)xcoord[i];
main_fill_poly_vertex_count++;
}
if(main_fill_polyY[main_fill_poly_vertex_count]!=main_fill_polyY[0] || main_fill_polyX[main_fill_poly_vertex_count] != main_fill_polyX[0]){
main_fill_polyX[main_fill_poly_vertex_count]=main_fill_polyX[0];
main_fill_polyY[main_fill_poly_vertex_count]=main_fill_polyY[0];
main_fill_poly_vertex_count++;
}
fill_end_fill(main_fill_poly_vertex_count-1,ymin,ymax);
}
}
}
void MIPS16 Free3DMemory(int i){
FreeMemorySafe((void *)&struct3d[i]->q_vertices);//array of original vertices
FreeMemorySafe((void *)&struct3d[i]->r_vertices); //array of rotated vertices
FreeMemorySafe((void *)&struct3d[i]->q_centroids);//array of original vertices
FreeMemorySafe((void *)&struct3d[i]->r_centroids); //array of rotated vertices
FreeMemorySafe((void *)&struct3d[i]->facecount); //number of vertices for each face
FreeMemorySafe((void *)&struct3d[i]->facestart); //index into the face_x_vert table of the start of a given face
FreeMemorySafe((void *)&struct3d[i]->fill); //fill colours
FreeMemorySafe((void *)&struct3d[i]->line); //line colours
FreeMemorySafe((void *)&struct3d[i]->colours);
FreeMemorySafe((void *)&struct3d[i]->face_x_vert); //list of vertices for each face
FreeMemorySafe((void *)&struct3d[i]->dots);
FreeMemorySafe((void *)&struct3d[i]->depth);
FreeMemorySafe((void *)&struct3d[i]->depthindex);
FreeMemorySafe((void *)&struct3d[i]->normals);
FreeMemorySafe((void *)&struct3d[i]->flags);
FreeMemorySafe((void *)&struct3d[i]);
}
void MIPS16 closeall3d(void){
int i;
for(i = 0; i < MAX3D; i++) {
if(struct3d[i]!=NULL){
Free3DMemory(i);
}
}
for(i=1; i<4;i++){
camera[i].viewplane=-32767;
}
}
void T_Mult(FLOAT3D *q1, FLOAT3D *q2, FLOAT3D *n){
FLOAT3D a1=q1[0],a2=q2[0],b1=q1[1],b2=q2[1],c1=q1[2],c2=q2[2],d1=q1[3],d2=q2[3];
n[0]=a1*a2-b1*b2-c1*c2-d1*d2;
n[1]=a1*b2+b1*a2+c1*d2-d1*c2;
n[2]=a1*c2-b1*d2+c1*a2+d1*b2;
n[3]=a1*d2+b1*c2-c1*b2+d1*a2;
n[4]=q1[4]*q2[4];
}
void T_Invert(FLOAT3D *q, FLOAT3D *n){
n[0]=q[0];
n[1]=-q[1];
n[2]=-q[2];
n[3]=-q[3];
n[4]=q[4];
}
void depthsort(FLOAT3D *farray, int n, int *index){
int i, j = n, s = 1;
int t;
FLOAT3D f;
while (s) {
s = 0;
for (i = 1; i < j; i++) {
if (farray[i] > farray[i - 1]) {
f = farray[i];
farray[i] = farray[i - 1];
farray[i - 1] = f;
s = 1;
if(index!=NULL){
t=index[i-1];
index[i-1]=index[i];
index[i]=t;
}
}
}
j--;
}
}
void q_rotate(s_quaternion *in, s_quaternion rotate, s_quaternion *out){
// PFlt(in->x);PFltComma(in->y);PFltComma(in->z);PFltComma(in->m);PRet();
s_quaternion temp, qtemp;
T_Mult((FLOAT3D *)&rotate, (FLOAT3D *)in, (FLOAT3D *)&temp);
T_Invert((FLOAT3D *)&rotate, (FLOAT3D *)&qtemp);
T_Mult((FLOAT3D *)&temp, (FLOAT3D *)&qtemp, (FLOAT3D *)out);
// PFlt(out->x);PFltComma(out->y);PFltComma(out->z);PFltComma(out->m);PRet();
}
void normalise(s_vector *v){
FLOAT3D n = sqrt3d((v->x) * (v->x) + (v->y) * (v->y) + (v->z) * (v->z) );
v->x /= n;
v->y /= n;
v->z /= n;
}
void display3d(int n, FLOAT3D x, FLOAT3D y, FLOAT3D z, int clear, int nonormals){
s_vector ray, lighting={0};
s_vector p1, p2, p3, U, V;
FLOAT3D x1, y1, z1, tmp;
FLOAT3D at, bt, ct, t, /*A=0, B=0, */C=1, D=-camera[struct3d[n]->camera].viewplane;
int maxH=VRes;
int maxW=HRes;
int vp, v, f, sortindex, csave=0, fsave=0;
if(struct3d[n]->vmax>4){ //needed for polygon fill
main_fill_polyX=(TFLOAT *)GetMemory(struct3d[n]->tot_face_x_vert * sizeof(TFLOAT));
main_fill_polyY=(TFLOAT *)GetMemory(struct3d[n]->tot_face_x_vert * sizeof(TFLOAT));
}
if(struct3d[n]->xmin!=32767 && clear)DrawRectangle(struct3d[n]->xmin,struct3d[n]->ymin,struct3d[n]->xmax,struct3d[n]->ymax,0);
struct3d[n]->xmin=32767;
struct3d[n]->ymin=32767;
struct3d[n]->xmax=-32767;
struct3d[n]->ymax=-32767;
short xcoord[MAX_POLYGON_VERTICES],ycoord[MAX_POLYGON_VERTICES];
struct3d[n]->distance=0.0;
for(f=0;f<struct3d[n]->nf;f++){
// calculate the surface normals for each face
vp=struct3d[n]->facestart[f];
p1.x=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+1]].x * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+1]].m + x;
p1.y=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+1]].y *struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+1]].m + y;
p1.z=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+1]].z * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+1]].m + z;
p2.x=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+2]].x * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+2]].m + x;
p2.y=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+2]].y * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+2]].m + y;
p2.z=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+2]].z * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+2]].m + z;
p3.x=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp]].x * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp]].m + x;
p3.y=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp]].y * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp]].m + y;
p3.z=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp]].z * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp]].m + z;
U.x=p2.x-p1.x; U.y=p2.y-p1.y; U.z=p2.z-p1.z;
V.x=p3.x-p1.x; V.y=p3.y-p1.y; V.z=p3.z-p1.z;
struct3d[n]->normals[f].x=U.y * V.z - U.z * V.y;
struct3d[n]->normals[f].y=U.z * V.x - U.x * V.z;
struct3d[n]->normals[f].z=U.x * V.y - U.y * V.x;
normalise(&struct3d[n]->normals[f]);
ray.x=p1.x - camera[struct3d[n]->camera].x;
ray.y=p1.y - camera[struct3d[n]->camera].y;
ray.z=p1.z - camera[struct3d[n]->camera].z;
normalise(&ray);
lighting.x=p1.x - struct3d[n]->light.x;
lighting.y=p1.y - struct3d[n]->light.y;
lighting.z=p1.z - struct3d[n]->light.z;
normalise(&lighting);
struct3d[n]->dots[f] = ray.x * struct3d[n]->normals[f].x + ray.y * struct3d[n]->normals[f].y + ray.z * struct3d[n]->normals[f].z;
tmp=struct3d[n]->r_centroids[f].m;
struct3d[n]->depth[f]=sqrt3d(
(struct3d[n]->r_centroids[f].z * tmp + z - camera[struct3d[n]->camera].z) *
(struct3d[n]->r_centroids[f].z * tmp + z - camera[struct3d[n]->camera].z) +
(struct3d[n]->r_centroids[f].y * tmp + y - camera[struct3d[n]->camera].y) *
(struct3d[n]->r_centroids[f].y * tmp + y - camera[struct3d[n]->camera].y) +
(struct3d[n]->r_centroids[f].x * tmp + x - camera[struct3d[n]->camera].x) *
(struct3d[n]->r_centroids[f].x * tmp + x - camera[struct3d[n]->camera].x)
);
struct3d[n]->depthindex[f]=f;
struct3d[n]->distance+=struct3d[n]->depth[f];
}
struct3d[n]->distance/=f;
// sort the distances from the faces to the camera
depthsort(struct3d[n]->depth, struct3d[n]->nf, struct3d[n]->depthindex);
// display the forward facing faces in the order of the furthest away first
for(f=0;f<struct3d[n]->nf;f++){
sortindex=struct3d[n]->depthindex[f];
vp=struct3d[n]->facestart[sortindex];
if(struct3d[n]->flags[sortindex] & 4)struct3d[n]->dots[sortindex]=-struct3d[n]->dots[sortindex];
if(nonormals || struct3d[n]->dots[sortindex]<0){
for(v=0;v<struct3d[n]->facecount[sortindex];v++){
x1=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+v]].x * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+v]].m + x;
y1=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+v]].y * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+v]].m + y;
z1=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+v]].z * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+v]].m + z;
// We now have the coordinates in real space so project them
at=x1-camera[struct3d[n]->camera].x;
bt=y1-camera[struct3d[n]->camera].y;
ct=z1-camera[struct3d[n]->camera].z;
t=-(/*A * x1 + B * y1*/ + C * z1 + D)/(/*A * at + B * bt + */C *ct);
xcoord[v]=x1+round3d(at*t)+(maxW>>1)-camera[struct3d[n]->camera].x-camera[struct3d[n]->camera].panx;
ycoord[v]=maxH-round3d(y1+bt*t)-1;
ycoord[v]-=(maxH>>1)-camera[struct3d[n]->camera].y-camera[struct3d[n]->camera].pany;
if(clear){
if(xcoord[v]>struct3d[n]->xmax)struct3d[n]->xmax=xcoord[v];
if(xcoord[v]<struct3d[n]->xmin)struct3d[n]->xmin=xcoord[v];
if(ycoord[v]>struct3d[n]->ymax)struct3d[n]->ymax=ycoord[v];
if(ycoord[v]<struct3d[n]->ymin)struct3d[n]->ymin=ycoord[v];
}
}
if((struct3d[n]->flags[sortindex] & 1) == 0) {
if(struct3d[n]->flags[sortindex] & 10) {
fsave=struct3d[n]->fill[sortindex];
csave=struct3d[n]->line[sortindex];
if(struct3d[n]->flags[sortindex] & 2)struct3d[n]->fill[sortindex]=0xFF0000;
if(struct3d[n]->flags[sortindex] & 8){
FLOAT3D lightratio=fabs3d(lighting.x * struct3d[n]->normals[sortindex].x + lighting.y * struct3d[n]->normals[sortindex].y + lighting.z * struct3d[n]->normals[sortindex].z);
lightratio=(lightratio*struct3d[n]->ambient)+struct3d[n]->ambient;
int red=(struct3d[n]->fill[sortindex] & 0xFF0000)>>16;
int green=(struct3d[n]->fill[sortindex] & 0xFF00)>>8;
int blue=(struct3d[n]->fill[sortindex] & 0xFF);
red=(round3d)((FLOAT3D)red*lightratio);
green=(round3d)((FLOAT3D)green*lightratio);
blue=(round3d)((FLOAT3D)blue*lightratio);
struct3d[n]->fill[sortindex]=(red<<16) | (green<<8) | blue;
red=(struct3d[n]->line[sortindex] & 0xFF0000)>>16;
green=(struct3d[n]->line[sortindex] & 0xFF00)>>8;
blue=(struct3d[n]->line[sortindex] & 0xFF);
red=(round3d)((FLOAT3D)red*lightratio);
green=(round3d)((FLOAT3D)green*lightratio);
blue=(round3d)((FLOAT3D)blue*lightratio);
struct3d[n]->line[sortindex]=(red<<16) | (green<<8) | blue;
}
}
DrawPolygon(n, xcoord, ycoord, sortindex);
if(struct3d[n]->flags[sortindex] & 10){
struct3d[n]->fill[sortindex]=fsave;
struct3d[n]->line[sortindex]=csave;
}
}
}
}
// Save information about how it was displayed for DRAW3D function and RESTORE command
struct3d[n]->current.x=x;
struct3d[n]->current.y=y;
struct3d[n]->current.z=z;
struct3d[n]->nonormals=nonormals;
if(struct3d[n]->vmax>4){ //needed for polygon fill
FreeMemory((unsigned char *)main_fill_polyX);
FreeMemory((unsigned char *)main_fill_polyY);
}
}
void MIPS16 diagnose3d(int n, FLOAT3D x, FLOAT3D y, FLOAT3D z, int sort){
s_vector ray, normals;
s_vector p1, p2, p3, U, V;
FLOAT3D tmp;
int vp, f, sortindex;
for(f=0;f<struct3d[n]->nf;f++){
// calculate the surface normals for each face
vp=struct3d[n]->facestart[f];
p1.x=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+1]].x * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+1]].m + x;
p1.y=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+1]].y *struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+1]].m + y;
p1.z=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+1]].z * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+1]].m + z;
p2.x=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+2]].x * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+2]].m + x;
p2.y=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+2]].y * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+2]].m + y;
p2.z=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+2]].z * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp+2]].m + z;
p3.x=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp]].x * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp]].m + x;
p3.y=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp]].y * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp]].m + y;
p3.z=struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp]].z * struct3d[n]->r_vertices[struct3d[n]->face_x_vert[vp]].m + z;
U.x=p2.x-p1.x; U.y=p2.y-p1.y; U.z=p2.z-p1.z;
V.x=p3.x-p1.x; V.y=p3.y-p1.y; V.z=p3.z-p1.z;
normals.x=U.y * V.z - U.z * V.y;
normals.y=U.z * V.x - U.x * V.z;
normals.z=U.x * V.y - U.y * V.x;
normalise(&normals);
ray.x=p1.x - camera[struct3d[n]->camera].x;
ray.y=p1.y - camera[struct3d[n]->camera].y;
ray.z=p1.z/* -camera[struct3d[n]->camera].z*/;
normalise(&ray);
struct3d[n]->dots[f] = ray.x * normals.x + ray.y * normals.y + ray.z * normals.z;
tmp=struct3d[n]->r_centroids[f].m;
struct3d[n]->depth[f]=sqrt3d(
(struct3d[n]->r_centroids[f].z * tmp + z - camera[struct3d[n]->camera].z) *
(struct3d[n]->r_centroids[f].z * tmp + z - camera[struct3d[n]->camera].z) +
(struct3d[n]->r_centroids[f].y * tmp + y - camera[struct3d[n]->camera].y) *
(struct3d[n]->r_centroids[f].y * tmp + y - camera[struct3d[n]->camera].y) +
(struct3d[n]->r_centroids[f].x * tmp + x - camera[struct3d[n]->camera].x) *
(struct3d[n]->r_centroids[f].x * tmp + x - camera[struct3d[n]->camera].x)
);
struct3d[n]->depthindex[f]=f;
}
// sort the dot products
depthsort(struct3d[n]->depth, struct3d[n]->nf, struct3d[n]->depthindex);
// display the forward facing faces in the order of the furthest away first
for(f=0;f<struct3d[n]->nf;f++){
if(sort)sortindex=struct3d[n]->depthindex[f];
else sortindex=f;
vp=struct3d[n]->facestart[sortindex];
MMPrintString("Face ");PInt(sortindex);
MMPrintString(" at distance ");PFlt(struct3d[n]->depth[f]);
MMPrintString(" dot product is ");PFlt(struct3d[n]->dots[sortindex]);
MMPrintString(" so the face is ");MMPrintString(struct3d[n]->dots[sortindex]>0 ? "Hidden" : "Showing");PRet();
}
}
/* @endcond */
void MIPS16 cmd_3D(void){
unsigned char *p;
if((p=checkstring(cmdline, (unsigned char *)"CREATE"))) {
// parameters are
// 3D object number (1 to MAX3D
// # of vertices = nv
// # of faces = nf
// vertex structure (nv)
// face array (face number, vertex number)
// colours array
// edge colour index array [nf]
// fill colour index array [nf]
// centroid structure [nf]
// normals structure [nf]
MMFLOAT *vertex;
TFLOAT tmp;
long long int *faces, *facecount, *facecountindex, *colours, *linecolour=NULL, *fillcolour=NULL;
getargs(&p,19,(unsigned char *)",");
if(argc<17)error("Argument count");
int c, colourcount=0, vp, v, f, fc=0, n=getint(argv[0],1,MAX3D);
if(struct3d[n]!=NULL)error("Object already exists");
int nv=getinteger(argv[2]);
if(nv<3)error("3D object must have a minimum of 3 vertices");
int nf=getinteger(argv[4]);
if(nf<1)error("3D object must have a minimum of 1 face");
int cam=getint(argv[6],1,MAXCAM);
if(parsefloatrarray(argv[8],&vertex,5,2,NULL,false)<nv)error("Vertex array too small");
if(parseintegerarray(argv[10],&facecount,6,1,NULL,false)<nf)error("Vertex count array too small");
facecountindex=facecount;
for(f=0;f<nf;f++)fc += (*facecountindex++);
if(parseintegerarray(argv[12],&faces,7,1,NULL,false)<fc)error("Face/vertex array too small");
colourcount=parseintegerarray(argv[14],&colours,8,1,NULL,false);
if(argc>=17 && *argv[16]){
if(parseintegerarray(argv[16],&linecolour,9,1,NULL,false)<nf)error("Line colour array too small");
}
if(argc==19){
if(parseintegerarray(argv[18],&fillcolour,10,1,NULL,false)<nf)error("Fill colour array too small");
}
// The data look valid so now create the object in memory
struct3d[n]=GetMemory(sizeof(struct D3D));
struct3d[n]->nf=nf;
struct3d[n]->nv=nv;
struct3d[n]->current.x=-32767;
struct3d[n]->current.y=-32767;
struct3d[n]->current.z=-32767;
struct3d[n]->xmin=32767;
struct3d[n]->ymin=32767;
struct3d[n]->xmax=-32767;
struct3d[n]->ymax=-32767;
struct3d[n]->camera=cam;
struct3d[n]->q_vertices=NULL;//array of original vertices
struct3d[n]->r_vertices=NULL; //array of rotated vertices
struct3d[n]->q_centroids=NULL;//array of original vertices
struct3d[n]->r_centroids=NULL; //array of rotated vertices
struct3d[n]->facecount=NULL; //number of vertices for each face
struct3d[n]->facestart=NULL; //index into the face_x_vert table of the start of a given face
struct3d[n]->fill=NULL; //fill colours
struct3d[n]->line=NULL; //line colours
struct3d[n]->colours=NULL;
struct3d[n]->face_x_vert=NULL; //list of vertices for each face
struct3d[n]->light.x=0;
struct3d[n]->light.y=0;
struct3d[n]->light.z=0;
struct3d[n]->ambient=0;
// load up things that have one entry per vertex
struct3d[n]->q_vertices=GetMemory(struct3d[n]->nv * sizeof(struct t_quaternion));
struct3d[n]->r_vertices=GetMemory(struct3d[n]->nv * sizeof(struct t_quaternion));
for(v=0;v<struct3d[n]->nv;v++){
FLOAT3D m=0.0;
struct3d[n]->q_vertices[v].x=(FLOAT3D)(*vertex++);
m+=struct3d[n]->q_vertices[v].x*struct3d[n]->q_vertices[v].x;
struct3d[n]->q_vertices[v].y=*vertex++;
m+=struct3d[n]->q_vertices[v].y*struct3d[n]->q_vertices[v].y;
struct3d[n]->q_vertices[v].z=*vertex++;
m+=struct3d[n]->q_vertices[v].z*struct3d[n]->q_vertices[v].z;
if(m){
m=sqrt(m);
struct3d[n]->q_vertices[v].x=struct3d[n]->q_vertices[v].x/m;
struct3d[n]->q_vertices[v].y=struct3d[n]->q_vertices[v].y/m;
struct3d[n]->q_vertices[v].z=struct3d[n]->q_vertices[v].z/m;
struct3d[n]->q_vertices[v].w=0.0;
struct3d[n]->q_vertices[v].m=m;
} else {
struct3d[n]->q_vertices[v].x=0;
struct3d[n]->q_vertices[v].y=0;
struct3d[n]->q_vertices[v].z=0;
struct3d[n]->q_vertices[v].w=0.0;
struct3d[n]->q_vertices[v].m=1.0;
}
memcpy(&struct3d[n]->r_vertices[v],&struct3d[n]->q_vertices[v], sizeof(s_quaternion));
}
struct3d[n]->tot_face_x_vert=0;
//load up things that have one entry per face
struct3d[n]->vmax=0;
struct3d[n]->facecount=GetMemory(struct3d[n]->nf * sizeof(uint16_t));
struct3d[n]->facestart=GetMemory(struct3d[n]->nf * sizeof(uint16_t));
struct3d[n]->fill=GetMemory(struct3d[n]->nf * sizeof(uint32_t));
struct3d[n]->line=GetMemory(struct3d[n]->nf * sizeof(uint32_t));
struct3d[n]->r_centroids=GetMemory(struct3d[n]->nf * sizeof(struct t_quaternion));
struct3d[n]->q_centroids=GetMemory(struct3d[n]->nf * sizeof(struct t_quaternion));
struct3d[n]->dots=GetMemory(struct3d[n]->nf * sizeof(MMFLOAT));
struct3d[n]->depth=GetMemory(struct3d[n]->nf * sizeof(MMFLOAT));
struct3d[n]->flags=GetMemory(struct3d[n]->nf * sizeof(uint8_t));
struct3d[n]->depthindex=GetMemory(struct3d[n]->nf * sizeof(int));
struct3d[n]->normals=GetMemory(struct3d[n]->nf * sizeof(struct SVD));
for(f=0;f<struct3d[n]->nf;f++){
struct3d[n]->facecount[f]=*facecount++;
if(struct3d[n]->facecount[f]<3){
Free3DMemory(n);
error("Vertex count less than 3 for face %",f+g_OptionBase);
}
if(struct3d[n]->facecount[f]>struct3d[n]->vmax)struct3d[n]->vmax=struct3d[n]->facecount[f];
struct3d[n]->facestart[f]=struct3d[n]->tot_face_x_vert;
struct3d[n]->tot_face_x_vert+=struct3d[n]->facecount[f];
}
// load up the array that holds all the face vertex information
struct3d[n]->face_x_vert=GetMemory(struct3d[n]->tot_face_x_vert * sizeof(uint16_t)); // allocate memory for the list of vertices per face
struct3d[n]->colours=GetMemory(colourcount * sizeof(uint32_t));
for(c=0; c<colourcount;c++){
struct3d[n]->colours[c]=(uint32_t)*colours++;
}
for(f=0;f<struct3d[n]->tot_face_x_vert;f++){
struct3d[n]->face_x_vert[f]=*faces++;
}
for(f=0;f<struct3d[n]->nf;f++){
if(linecolour!=NULL){
int index=(*linecolour++) - g_OptionBase;
if(index>=colourcount || index<0){
Free3DMemory(n);
error("Edge colour Index %",index);
}
struct3d[n]->line[f]=struct3d[n]->colours[index];
} else struct3d[n]->line[f]=gui_fcolour;
if(fillcolour!=NULL){
int index=(*fillcolour++) - g_OptionBase;
if(index>=colourcount || index<0){
Free3DMemory(n);
error("Fill colour Index %",index);
}
struct3d[n]->fill[f]=struct3d[n]->colours[index];
} else struct3d[n]->fill[f]=0xFFFFFFFF;
FLOAT3D x=0, y=0, z=0, scale;
vp=struct3d[n]->facestart[f];
// calculate the centroids of each face
for(v=0;v<struct3d[n]->facecount[f];v++){
tmp=struct3d[n]->q_vertices[struct3d[n]->face_x_vert[vp+v]].m;
x+=struct3d[n]->q_vertices[struct3d[n]->face_x_vert[vp+v]].x*tmp;
y+=struct3d[n]->q_vertices[struct3d[n]->face_x_vert[vp+v]].y*tmp;
z+=struct3d[n]->q_vertices[struct3d[n]->face_x_vert[vp+v]].z*tmp;
}
x/=(FLOAT3D)struct3d[n]->facecount[f];
y/=(FLOAT3D)struct3d[n]->facecount[f];
z/=(FLOAT3D)struct3d[n]->facecount[f];
struct3d[n]->q_centroids[f].x=x;
struct3d[n]->q_centroids[f].y=y;
struct3d[n]->q_centroids[f].z=z;
scale=sqrt(struct3d[n]->q_centroids[f].x*struct3d[n]->q_centroids[f].x +
struct3d[n]->q_centroids[f].y*struct3d[n]->q_centroids[f].y +
struct3d[n]->q_centroids[f].z*struct3d[n]->q_centroids[f].z);
struct3d[n]->q_centroids[f].x/=scale;
struct3d[n]->q_centroids[f].y/=scale;
struct3d[n]->q_centroids[f].z/=scale;
struct3d[n]->q_centroids[f].m=scale;
struct3d[n]->q_centroids[f].w=0;
memcpy(&struct3d[n]->r_centroids[f],&struct3d[n]->q_centroids[f], sizeof(s_quaternion));
}
return;
} else if((p=checkstring(cmdline, (unsigned char *)"DIAGNOSE"))) {
getargs(&p,9,(unsigned char *)",");
if(argc<7)error("Argument count");
int n=getint(argv[0],1,MAX3D);
int x=getint(argv[2],-32766,32766);
int y=getint(argv[4],-32766,32766);
int z=getinteger(argv[6]);
int sort=1;
if(argc==9)sort=getint(argv[8],0,1);
if(struct3d[n]==NULL)error("Object % does not exist",n);
if(camera[struct3d[n]->camera].viewplane==-32767)error("Camera position not defined");
diagnose3d(n, x, y, z, sort);
return;
} else if((p=checkstring(cmdline, (unsigned char *)"LIGHT"))) {
getargs(&p,9,(unsigned char *)",");
if(argc!=9)error("Argument count");
int n=getint(argv[0],1,MAX3D);
struct3d[n]->light.x=getint(argv[2],-32766,32766);
struct3d[n]->light.y=getint(argv[4],-32766,32766);
struct3d[n]->light.z=getint(argv[6],-32766,32766);
struct3d[n]->ambient=(FLOAT3D)(getint(argv[8],0,100))/100.0;
return;
} else if((p=checkstring(cmdline, (unsigned char *)"SHOW"))) {
getargs(&p,9,(unsigned char *)",");
if(argc<7)error("Argument count");
int n=getint(argv[0],1,MAX3D);
int x=getint(argv[2],-32766,32766);
int y=getint(argv[4],-32766,32766);
int z=getinteger(argv[6]);
int nonormals=0;
if(argc==9)nonormals=getint(argv[8],0,1);
if(struct3d[n]==NULL)error("Object % does not exist",n);
if(camera[struct3d[n]->camera].viewplane==-32767)error("Camera position not defined");
display3d(n, x, y, z, 1, nonormals);
return;
} else if((p=checkstring(cmdline, (unsigned char *)"SET FLAGS"))) {
int i, face, nbr;
getargs(&p, ((MAX_ARG_COUNT-1) * 2) - 1, (unsigned char *)",");
if((argc & 0b11) != 0b11) error("Invalid syntax");
int n=getint(argv[0],1,MAX3D);
int flag=getint(argv[2],0,255);
// step over the equals sign and get the value for the assignment
for(i = 4; i < argc; i += 4) {
face = getinteger(argv[i]);
nbr = getinteger(argv[i + 2]);
if(nbr <= 0 || nbr>struct3d[n]->nf-face) error("Invalid argument");
while(--nbr>=0) {
struct3d[n]->flags[face+nbr]=flag;
}
}
} else if((p=checkstring(cmdline, (unsigned char *)"ROTATE"))) {
int i, n, v, f;
s_quaternion q1;
MMFLOAT *q=NULL;
getargs(&p, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)","); // getargs macro must be the first executable stmt in a block
if((argc & 0x01 || argc<3) == 0) error("Argument count");
if(parsefloatrarray(argv[0],&q,1,1,NULL,true)!=5)error("Argument 1 must be a 5 element floating point array");
q1.w=(FLOAT3D)(*q++);
q1.x=(FLOAT3D)(*q++);
q1.y=(FLOAT3D)(*q++);
q1.z=(FLOAT3D)(*q++);
q1.m=(FLOAT3D)(*q);
for(i = 2; i < argc; i += 2) {
n=getint(argv[i],1,MAX3D);
if(struct3d[n]==NULL)error("Object % does not exist",n);
for(v=0;v<struct3d[n]->nv;v++){
q_rotate(&struct3d[n]->q_vertices[v],q1,&struct3d[n]->r_vertices[v]);
}
for(f=0;f<struct3d[n]->nf;f++){
q_rotate(&struct3d[n]->q_centroids[f],q1,&struct3d[n]->r_centroids[f]);
}
}
return;
} else if((p=checkstring(cmdline, (unsigned char *)"HIDE ALL"))) {
for(int i=1;i<=MAX3D;i++){
if(struct3d[i]!=NULL && struct3d[i]->xmin!=32767){
DrawRectangle(struct3d[i]->xmin,struct3d[i]->ymin,struct3d[i]->xmax,struct3d[i]->ymax,0);
struct3d[i]->xmin=32767;
struct3d[i]->ymin=32767;
struct3d[i]->xmax=-32767;
struct3d[i]->ymax=-32767;
}
}
return;
} else if((p=checkstring(cmdline, (unsigned char *)"RESET"))) {
int i, n;
int v, f;
getargs(&p, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)","); // getargs macro must be the first executable stmt in a block
if((argc & 0x01 || argc<3) == 0) error("Argument count");
for(i = 0; i < argc; i += 2) {
n=getint(argv[i],1,MAX3D);
for(v=0;v<struct3d[n]->nv;v++){
memcpy(&struct3d[n]->q_vertices[v],&struct3d[n]->r_vertices[v], sizeof(s_quaternion));
}
for(f=0;f<struct3d[n]->nf;f++){
memcpy(&struct3d[n]->q_centroids[f],&struct3d[n]->r_centroids[f], sizeof(s_quaternion));
}
}
return;
} else if((p=checkstring(cmdline, (unsigned char *)"HIDE"))) {
int i, n;
getargs(&p, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)","); // getargs macro must be the first executable stmt in a block
if((argc & 0x01 || argc<3) == 0) error("Argument count");
for(i = 0; i < argc; i += 2) {
n=getint(argv[i],1,MAX3D);
if(struct3d[n]==NULL)error("Object % does not exist",n);
if(struct3d[n]->xmin==32767)return;
DrawRectangle(struct3d[n]->xmin,struct3d[n]->ymin,struct3d[n]->xmax,struct3d[n]->ymax,0);
struct3d[n]->xmin=32767;
struct3d[n]->ymin=32767;
struct3d[n]->xmax=-32767;
struct3d[n]->ymax=-32767;
}
return;
} else if((p=checkstring(cmdline, (unsigned char *)"RESTORE"))) {
int i, n;
getargs(&p, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)","); // getargs macro must be the first executable stmt in a block
if((argc & 0x01 || argc<3) == 0) error("Argument count");
for(i = 0; i < argc; i += 2) {
n=getint(argv[i],1,MAX3D);
if(struct3d[n]==NULL)error("Object % does not exist",n);
if(struct3d[n]->xmin!=32767)error("Object % is not hidden",n);
display3d(n, struct3d[n]->current.x, struct3d[n]->current.y, struct3d[n]->current.z, 1, struct3d[n]->nonormals);
}
return;
} else if((p=checkstring(cmdline, (unsigned char *)"WRITE"))) {
getargs(&p,9,(unsigned char *)",");
if(argc<7)error("Argument count");
int n=getint(argv[0],1,MAX3D);
int x=getint(argv[2],-32766,32766);
int y=getint(argv[4],-32766,32766);
int z=getinteger(argv[6]);
int nonormals=0;
if(argc==9)nonormals=getint(argv[8],0,1);
if(struct3d[n]==NULL)error("Object % does not exist",n);
if(camera[struct3d[n]->camera].viewplane==-32767)error("Camera position not defined");
display3d(n, x, y, z, 0, nonormals);
return;
} else if((p=checkstring(cmdline, (unsigned char *)"CLOSE ALL"))) {
closeall3d();
return;
} else if((p=checkstring(cmdline, (unsigned char *)"CLOSE"))) {
int i, n;
getargs(&p, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)","); // getargs macro must be the first executable stmt in a block
if((argc & 0x01 || argc<3) == 0) error("Argument count");
for(i = 0; i < argc; i += 2) {
n=getint(argv[i],1,MAX3D);
if(struct3d[n]==NULL)error("Object % does not exist",n);
if(struct3d[n]->xmin!=32767)DrawRectangle(struct3d[n]->xmin,struct3d[n]->ymin,struct3d[n]->xmax,struct3d[n]->ymax,0);
Free3DMemory(n);
}
return;
} else if((p=checkstring(cmdline, (unsigned char *)"CAMERA"))) {
getargs(&p,11,(unsigned char *)",");
if(argc<3)error("Argument count");
int n=getint(argv[0],1,MAXCAM);
camera[n].viewplane=getnumber(argv[2]);
camera[n].x=(FLOAT3D)0;
camera[n].y=(FLOAT3D)0;
camera[n].panx=(FLOAT3D)0;
camera[n].pany=(FLOAT3D)0;
camera[n].z=0.0;
if(argc>=5 && *argv[4]) camera[n].x=getnumber(argv[4]);
if(camera[n].x > 32766 || camera[n].x < -32766 )error("Valid is -32766 to 32766");
if(argc>=7 && *argv[6]) camera[n].y=getnumber(argv[6]);
if(camera[n].y > 32766 || camera[n].x < -32766 )error("Valid is -32766 to 32766");
if(argc>=9 && *argv[8]) camera[n].panx=getint(argv[8],-32766-camera[n].x,32766-camera[n].x);
if(argc==11 )camera[n].pany=getint(argv[10],-32766-camera[n].y,32766-camera[n].y);
return;
} else {
error("Syntax");
}
}
void MIPS16 fun_3D(void){
unsigned char *p;
if((p=checkstring(ep, (unsigned char *)"XMIN"))) {
getargs(&p,1,(unsigned char *)",");
int n=getint(argv[0],1,MAX3D);
if(struct3d[n]==NULL)error("Object does not exist");
fret=struct3d[n]->xmin;
} else if((p=checkstring(ep, (unsigned char *)"XMAX"))) {
getargs(&p,1,(unsigned char *)",");
int n=getint(argv[0],1,MAX3D);
if(struct3d[n]==NULL)error("Object does not exist");
fret=struct3d[n]->xmax;
} else if((p=checkstring(ep, (unsigned char *)"YMIN"))) {
getargs(&p,1,(unsigned char *)",");
int n=getint(argv[0],1,MAX3D);
if(struct3d[n]==NULL)error("Object does not exist");
fret=struct3d[n]->ymin;
} else if((p=checkstring(ep, (unsigned char *)"YMAX"))) {
getargs(&p,1,(unsigned char *)",");
int n=getint(argv[0],1,MAX3D);
if(struct3d[n]==NULL)error("Object does not exist");
fret=struct3d[n]->ymax;
} else if((p=checkstring(ep, (unsigned char *)"X"))) {
getargs(&p,1,(unsigned char *)",");
int n=getint(argv[0],1,MAX3D);
if(struct3d[n]==NULL)error("Object does not exist");
fret=struct3d[n]->current.x;
} else if((p=checkstring(ep, (unsigned char *)"Y"))) {
getargs(&p,1,(unsigned char *)",");
int n=getint(argv[0],1,MAX3D);
if(struct3d[n]==NULL)error("Object does not exist");
fret=struct3d[n]->current.y;
} else if((p=checkstring(ep, (unsigned char *)"DISTANCE"))) {
getargs(&p,1,(unsigned char *)",");
int n=getint(argv[0],1,MAX3D);
if(struct3d[n]==NULL)error("Object does not exist");
fret=struct3d[n]->distance;
} else if((p=checkstring(ep, (unsigned char *)"Z"))) {
getargs(&p,1,(unsigned char *)",");
int n=getint(argv[0],1,MAX3D);
if(struct3d[n]==NULL)error("Object does not exist");
fret=struct3d[n]->current.z;
} else error("Syntax");
targ=T_NBR;
}
/*
* @cond
* The following section will be excluded from the documentation.
*/
#endif
#ifdef PICOMITEVGA
void closeframebuffer(char layer){
if(layer=='A')WriteBuf=DisplayBuf;
if(FrameBuf!=DisplayBuf && (layer=='A' || layer=='F')){
if(WriteBuf==FrameBuf)WriteBuf=DisplayBuf;
switch(DISPLAY_TYPE){
case SCREENMODE1:
case SCREENMODE2:
#ifdef rp2350
FrameBuf=DisplayBuf;
#else
FreeMemory((void *)FrameBuf);
#endif
break;
#ifdef rp2350
case SCREENMODE3:
FreeMemory((void *)FrameBuf);
break;
#ifdef HDMI
case SCREENMODE4:
case SCREENMODE5:
FreeMemory((void *)FrameBuf);
break;
#endif
#endif
}
}
if(LayerBuf!=DisplayBuf && (layer=='A' || layer=='L')){
if(WriteBuf==LayerBuf)WriteBuf=DisplayBuf;
volatile unsigned char *temp= LayerBuf;
switch(DISPLAY_TYPE){
case SCREENMODE2:
transparent=0;
case SCREENMODE1:
#ifdef rp2350
LayerBuf=DisplayBuf;
#else
LayerBuf=DisplayBuf;
FreeMemory((void *)temp);
#endif
break;
#ifdef rp2350
case SCREENMODE3:
LayerBuf=DisplayBuf;
FreeMemory((void *)temp);
break;
#ifdef HDMI
case SCREENMODE4:
LayerBuf=DisplayBuf;
FreeMemory((void *)temp);
break;
case SCREENMODE5:
LayerBuf=DisplayBuf;
transparent=0;
break;
#endif
#endif
}
}
if(SecondFrame!=DisplayBuf && (layer=='A' || layer=='2')){
FreeMemory((void *)SecondFrame);
}
if(SecondLayer!=DisplayBuf && (layer=='A' || layer=='T')){
if(WriteBuf==LayerBuf)WriteBuf=DisplayBuf;
volatile unsigned char *temp= SecondLayer;
switch(DISPLAY_TYPE){
case SCREENMODE2:
transparents=0;
SecondLayer=DisplayBuf;
break;
case SCREENMODE1:
SecondLayer=DisplayBuf;
FreeMemory((void *)temp);
break;
#ifdef rp2350
case SCREENMODE3:
SecondLayer=DisplayBuf;
FreeMemory((void *)temp);
break;
#ifdef HDMI
case SCREENMODE4:
SecondLayer=DisplayBuf;
FreeMemory((void *)temp);
break;
case SCREENMODE5:
SecondLayer=DisplayBuf;
transparents=0;
break;
#endif
#endif
}
}
WriteBuf=(unsigned char *)FRAMEBUFFER;
DisplayBuf=(unsigned char *)FRAMEBUFFER;
LayerBuf=(unsigned char *)FRAMEBUFFER;
FrameBuf=(unsigned char *)FRAMEBUFFER;
SecondLayer=(unsigned char *)FRAMEBUFFER;
SecondFrame=(unsigned char *)FRAMEBUFFER;
transparent=0;
}
/* @endcond */
void cmd_framebuffer(void){
/*
RP2040 version support just modes 1 and 2
RP2350 vversions support modes 1 to 5
All modes can have a framebuffer and a layer buffer but only modes 2 and 5 automatically display the layer buffer over the top of the main display
In all other cases it is just another framebuffer that can be used to build up images to be copied to the main display
For VGA/HDMI Layer buffers and framebuffers have exactly the same resolution as the main display (unlike TFT displays)
For RP2350 Modes 1 and 2 both buffers are in the allocated Video Memory (640x240 bytes == 320x240x2)
For RP2350 Mode 5 the layer buffer is in the allocated video memory
All other buffers are allocated out of variable space using GetMemory
NB: for RP2350 with PSRAM buffers may be allocated in the slower external memory
Buffer sizes are:
Normal:
#define MODE1SIZE_S VMaxH*VMaxV/8
#define MODE2SIZE_S 320*240/2
#define MODE3SIZE_S VMaxH*VMaxV/2
#define MODE4SIZE_S 320*240*2
#define MODE5SIZE_S 320*240
Widescreen:
#define MODE1SIZE_W 1280 *720 /8
#define MODE2SIZE_W (1280/4) * (720/4)/2
#define MODE3SIZE_W (1280/2) * (720/2)/2
#define MODE5SIZE_W (1280/4) * (720/4)
XGA:
#define MODE1SIZE_W `1024 *768 /8
#define MODE2SIZE_W (1024/4) * (768/4)/2
#define MODE3SIZE_W (1024/2) * (768/2)/2
#define MODE5SIZE_W (1024/4) * (768/4)
*/
unsigned char *p;
#ifdef rp2350
if((p=checkstring(cmdline, (unsigned char *)"CREATE 2"))) {
int colour=0;
if(SecondFrame==DisplayBuf){
getargs(&p,1,(unsigned char *)",");
switch(DISPLAY_TYPE){
case SCREENMODE2:
case SCREENMODE1:
SecondFrame=GetMemory(ScreenSize);
break;
#ifdef rp2350
case SCREENMODE3:
SecondFrame=GetMemory(ScreenSize);
break;
#ifdef HDMI
case SCREENMODE4:
SecondFrame=GetMemory(ScreenSize);
break;
case SCREENMODE5:
SecondFrame=GetMemory(ScreenSize);
break;
#endif
#endif
}
} else error("Framebuffer 2 already exists");
memset((void *)SecondFrame,colour,ScreenSize);
} else
#endif
if((p=checkstring(cmdline, (unsigned char *)"CREATE"))) {
if(FrameBuf==DisplayBuf){
switch(DISPLAY_TYPE){
case SCREENMODE1:
case SCREENMODE2:
#ifdef rp2350
if(ScreenSize<framebuffersize/3)FrameBuf=DisplayBuf+2*ScreenSize;
else FrameBuf=GetMemory(ScreenSize);
#else
FrameBuf=GetMemory(ScreenSize);
#endif
break;
#ifdef rp2350
case SCREENMODE3:
FrameBuf=GetMemory(ScreenSize);
break;
#ifdef HDMI
case SCREENMODE4:
case SCREENMODE5:
FrameBuf=GetMemory(ScreenSize);
break;
#endif
#endif
}
} else error("Framebuffer already exists");
memset((void *)FrameBuf,0,ScreenSize);
#ifdef rp2350
} else if((p=checkstring(cmdline, (unsigned char *)"LAYER TOP"))) {
int colour=0;
if(SecondLayer==DisplayBuf){
getargs(&p,1,(unsigned char *)",");
switch(DISPLAY_TYPE){
case SCREENMODE2:
if(argc==1)transparents=getint(argv[0],0,15);
colour=transparents | (transparents<<4);
if(ScreenSize<framebuffersize/4)SecondLayer=DisplayBuf+3*ScreenSize;
else SecondLayer=GetMemory(ScreenSize);
break;
case SCREENMODE1:
SecondLayer=GetMemory(ScreenSize);
break;
case SCREENMODE3:
if(argc==1)transparents=getint(argv[0],0,15);
SecondLayer=GetMemory(ScreenSize);
if(SecondLayer>=(uint8_t *)PSRAMbase && SecondLayer< (uint8_t *)(PSRAMbase + 1024*1024*16)){
FreeMemory((void *)SecondLayer);
error("Second Layer must be in tightly coupled RAM, declare before other variables");
}
colour=transparents | (transparents<<4);
break;
#ifdef HDMI
case SCREENMODE4:
SecondLayer=GetMemory(ScreenSize);
break;
case SCREENMODE5:
SecondLayer=GetMemory(ScreenSize);
if(SecondLayer>=(uint8_t *)PSRAMbase && SecondLayer< (uint8_t *)(PSRAMbase + 1024*1024*16)){
FreeMemory((void *)SecondLayer);
error("Second Layer must be in tightly coupled RAM, declare before other variables");
}
if(argc==1)transparents=getint(argv[0],0,255);
colour=transparents;
break;
#endif
}
} else error("Framebuffer already exists");
memset((void *)SecondLayer,colour,ScreenSize);
#endif
} else if((p=checkstring(cmdline, (unsigned char *)"LAYER"))) {
int colour=0;
if(LayerBuf==DisplayBuf){
getargs(&p,1,(unsigned char *)",");
switch(DISPLAY_TYPE){
case SCREENMODE2:
if(argc==1)transparent=getint(argv[0],0,15);
colour=transparent | (transparent<<4);
case SCREENMODE1:
#ifdef rp2350
if(ScreenSize<framebuffersize/2)LayerBuf=DisplayBuf+ScreenSize;
else LayerBuf=GetMemory(ScreenSize);
#else
LayerBuf=GetMemory(ScreenSize);
#endif
break;
#ifdef rp2350
case SCREENMODE3:
if(argc==1)transparent=getint(argv[0],0,15);
LayerBuf=GetMemory(ScreenSize);
colour=transparent | (transparent<<4);
break;
#ifdef HDMI
case SCREENMODE4:
LayerBuf=GetMemory(ScreenSize);
if(argc==1)RGBtransparent=RGB555(getColour((char *)argv[0],0));
else RGBtransparent=0;
break;
case SCREENMODE5:
if(ScreenSize<framebuffersize/2)LayerBuf=DisplayBuf+ScreenSize;
else LayerBuf=GetMemory(ScreenSize);
if(argc==1)transparent=getint(argv[0],0,255);
colour=transparent;
break;
#endif
#endif
}
#ifdef rp2350
if(LayerBuf>(uint8_t *)PSRAMbase && LayerBuf< (uint8_t *)(PSRAMbase + 1024*1024*16)){
FreeMemory((void *)LayerBuf);
error("Layer Buffer must be in tightly coupled RAM, declare before other variables");
}
#endif
} else error("Framebuffer already exists");
if(DISPLAY_TYPE!=SCREENMODE4)memset((void *)LayerBuf,colour,ScreenSize);
else {
uint16_t *p=(uint16_t *)LayerBuf;
for(int i=0;i<HRes*VRes;i++)*p++=RGBtransparent;
}
} else if((p=checkstring(cmdline, (unsigned char *)"CLOSE"))) {
if(checkstring(p, (unsigned char *)"F")){
closeframebuffer('F');
} else if(checkstring(p, (unsigned char *)"L")){
closeframebuffer('T');
#ifdef rp2350
} else if(checkstring(p, (unsigned char *)"T")){
closeframebuffer('L');
} else if(checkstring(p, (unsigned char *)"2")){
closeframebuffer('2');
#endif
} else closeframebuffer('A');
} else if((p=checkstring(cmdline, (unsigned char *)"WRITE"))) {
if(checkstring(p, (unsigned char *)"N"))WriteBuf=DisplayBuf;
else if(checkstring(p, (unsigned char *)"L")){
if(LayerBuf==DisplayBuf)error("Layer not created");
WriteBuf=LayerBuf;
}
#ifdef rp2350
else if(checkstring(p, (unsigned char *)"T")){
if(SecondLayer==DisplayBuf)error("Layer 2 not created");
WriteBuf=SecondLayer;
}
else if(checkstring(p, (unsigned char *)"2")){
if(SecondFrame==DisplayBuf)error("Frame 2 not created");
WriteBuf=SecondFrame;
}
#endif
else if(checkstring(p, (unsigned char *)"F")){
if(FrameBuf==DisplayBuf)error("Frame buffer not created");
WriteBuf=FrameBuf;
}
else {
getargs(&p,1,(unsigned char *)",");
char *q=(char *)getCstring(argv[0]);
if(strcasecmp(q,"N")==0)WriteBuf=DisplayBuf;
else if(strcasecmp(q,"L")==0){
if(LayerBuf==DisplayBuf)error("Layer not created");
WriteBuf=LayerBuf;
} else if(strcasecmp(q,"F")==0){
if(FrameBuf==DisplayBuf)error("Frame buffer not created");
WriteBuf=FrameBuf;
} else if(strcasecmp(q,"2")==0){
if(SecondFrame==DisplayBuf)error("Frame buffer 2 not created");
WriteBuf=SecondFrame;
} else if(strcasecmp(q,"T")==0){
if(SecondLayer==DisplayBuf)error("Layer Top not created");
WriteBuf=SecondLayer;
}
else error("Syntax");
}
} else if((p=checkstring(cmdline, (unsigned char *)"WAIT"))) {
#ifdef HDMI
while(v_scanline!=0){}
#else
while(QVgaScanLine!=0){}
#endif
} else if((p=checkstring(cmdline, (unsigned char *)"COPY"))) {
getargs(&p,5,(unsigned char *)",");
if(!(argc==3 || argc==5))error("Syntax");
volatile uint8_t *s=NULL,*d=NULL;
if(checkstring(argv[0],(unsigned char *)"N"))s=DisplayBuf;
else if(checkstring(argv[0],(unsigned char *)"L"))s=LayerBuf;
else if(checkstring(argv[0],(unsigned char *)"F"))s=FrameBuf;
else if(checkstring(argv[0],(unsigned char *)"2"))s=SecondFrame;
else if(checkstring(argv[0],(unsigned char *)"T"))s=SecondLayer;
else error("Syntax");
if(checkstring(argv[2],(unsigned char *)"N"))d=DisplayBuf;
else if(checkstring(argv[2],(unsigned char *)"L"))d=LayerBuf;
else if(checkstring(argv[2],(unsigned char *)"F"))d=FrameBuf;
else if(checkstring(argv[2],(unsigned char *)"2"))d=SecondFrame;
else if(checkstring(argv[2],(unsigned char *)"T"))d=SecondLayer;
else error("Syntax");
if(argc==5){
if(checkstring(argv[4],(unsigned char *)"B")){
#ifdef HDMI
while(v_scanline!=0){}
#else
while(QVgaScanLine!=0){}
#endif
} else error("Syntax");
}
if(d!=s)
// #ifdef rp2350
// _Z10copy_wordsPKmPmm((uint32_t *)s, (uint32_t *)d, ScreenSize>>2);
// #else
memcpy((void *)d,(void *)s,ScreenSize);
// #endif
else error("Buffer not created");
} else error("Syntax");
}
#endif