From 06ca429d824f8898cbe5cbed26fa18d0e8a56a08 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Fri, 2 Dec 2022 20:25:47 -0600 Subject: [PATCH] Start of variable support. Not working properly yet. --- include/vecparse.h | 22 +- src/vecparse.c | 582 +++++++++++++++++++++++++++++---------------- src/vector.c | 244 +++++++++++++++---- 3 files changed, 592 insertions(+), 256 deletions(-) diff --git a/include/vecparse.h b/include/vecparse.h index 71f79e1..8c4effa 100644 --- a/include/vecparse.h +++ b/include/vecparse.h @@ -37,10 +37,30 @@ enum ParserKeywordE { PARSE_PALETTE, PARSE_PLOT, PARSE_RECTANGLE, - PARSE_RESET + PARSE_RESET, + // After this are keywords for things that aren't commands but still need to emit bytecode. + PARSE_MATH, + PARSE_LABEL }; typedef enum ParserKeywordE ParserKeywordT; +enum ParserMathE { + MATH_NONE = 0, + MATH_ASSIGN, + MATH_ADD, + MATH_SUBTRACT, + MATH_MULTIPLY, + MATH_DIVIDE, + MATH_MOD, + MATH_POW, + MATH_SQRT, + MATH_ABS, + MATH_COS, + MATH_SIN, + MATH_TAN +}; +typedef enum ParserMathE ParserMathT; + typedef struct PointS { int x; diff --git a/src/vecparse.c b/src/vecparse.c index 8d6115e..b331cc8 100644 --- a/src/vecparse.c +++ b/src/vecparse.c @@ -20,24 +20,33 @@ */ +#include #include "common.h" #include "vecparse.h" -typedef struct CommandsS { +#define IS_NUMBER(n) (n & 0x8000 ? TRUE : FALSE) + + +typedef struct KeywordsS { char *command; - ParserKeywordT keyword; -} CommandsT; + ParserKeywordT id; +} KeywordsT; + +typedef struct OperatorsS { + char *operator; + ParserMathT id; +} OperatorsT; static void ensureBufferSize(VecByteCodeT *bytecode, int needed); -static void outputByte(VecByteCodeT *bytecode, unsigned char byte); +static void outputByte(VecByteCodeT *bytecode, unsigned short word); static void outputWord(VecByteCodeT *bytecode, unsigned short word); -static gboolean parserGetNextValue(char *token, char **valueEnd, int *x); +static gboolean parserGetNextValue(char *token, char **valueEnd, char **variables, int *x); static gboolean parserGetWord(char *word, char **tokenEnd); -static gboolean parserGetX(char **tokenEnd, int *x); -static gboolean parserGetXY(char **tokenEnd, int *x, int *y); -static gboolean parserGetXYZ(char **tokenEnd, int *x, int *y, int *z); +static gboolean parserGetX(char **tokenEnd, char **variables, int *x); +static gboolean parserGetXY(char **tokenEnd, char **variables, int *x, int *y); +static gboolean parserGetXYZ(char **tokenEnd, char **variables, int *x, int *y, int *z); static void ensureBufferSize(VecByteCodeT *bytecode, int needed) { @@ -55,8 +64,16 @@ static void ensureBufferSize(VecByteCodeT *bytecode, int needed) { } -static void outputByte(VecByteCodeT *bytecode, unsigned char byte) { +static void outputByte(VecByteCodeT *bytecode, unsigned short word) { + unsigned char byte = (unsigned char)word; + ensureBufferSize(bytecode, 1); + + // If the word passed in is a variable, set the MSb in the byte as well. + if (word & 0x8000) { + byte |= 0x80; + } + bytecode->bytes[bytecode->length++] = byte; } @@ -68,18 +85,45 @@ static void outputWord(VecByteCodeT *bytecode, unsigned short word) { } -static gboolean parserGetNextValue(char *token, char **valueEnd, int *x) { +static gboolean parserGetNextValue(char *token, char **valueEnd, char **variables, int *x) { char *value; char *endPtr = NULL; + int index; // Return next value in a comma separated list. - //***TODO*** Variable support. value = strtok_r(token, ",", valueEnd); if (value == NULL) return FALSE; - errno = 0; endPtr = NULL; - *x = (int)strtol(value, &endPtr, 10); - if (errno != 0 || *endPtr != 0) return FALSE; + + // Is it a variable or number? + if (strlen(value) > 1 && value[0] == '%') { + + // It's a variable. Do we know it? + *x = -1; + for (index=0; index 1 && value[0] == '%') { + + // It's a variable. Do we know it? + *x = -1; + for (index=0; index 1 && token[0] == '%') { - case PARSE_NONE: - // Won't happen, but silences an error. - break; + // We're doing some kind of math. - case PARSE_BOX: - // Box (value),(value) to (value),(value) - if (!parserGetXY(&tokenEnd, &x1, &y1)) break; - if (!parserGetWord("TO", &tokenEnd)) break; - if (!parserGetXY(&tokenEnd, &x2, &y2)) break; - if (x2 < x1 || x1 < 0 || x2 < 0 || x1 > 319 || x2 > 319) break; - if (y2 < y1 || y1 < 0 || y2 < 0 || y1 > 199 || y2 > 199) break; - outputByte(bytecode, PARSE_BOX); - outputWord(bytecode, x1); - outputWord(bytecode, y1); - outputWord(bytecode, x2); - outputWord(bytecode, y2); - lineOkay = TRUE; - break; + // Look up variable index in table, or add it if needed. + // Variable index is stored in y1 for later. + y1 = -1; + for (y2=0; y2 319) break; - if (y1 < 0 || y1 > 199) break; - outputByte(bytecode, PARSE_CIRCLE); - outputWord(bytecode, y2); - outputWord(bytecode, x1); - outputWord(bytecode, y1); - lineOkay = TRUE; - break; - - case PARSE_CLEAR: - outputByte(bytecode, PARSE_CLEAR); - lineOkay = TRUE; - break; - - case PARSE_COLOR: - // Color (short) - if (!parserGetX(&tokenEnd, &x1)) break; - if (x1 < 0 || x1 > 15) break; - outputByte(bytecode, PARSE_COLOR); - outputByte(bytecode, x1); - lineOkay = TRUE; - break; - - case PARSE_COMMENT: - // Eat the rest of the line. - while (token != NULL) { - token = strtok_r(NULL, " ", &tokenEnd); - } - lineOkay = TRUE; - break; - - case PARSE_ELLIPSE: - // Ellipse (value),(value) to (value),(value) - if (!parserGetXY(&tokenEnd, &x1, &y1)) break; - if (!parserGetWord("TO", &tokenEnd)) break; - if (!parserGetXY(&tokenEnd, &x2, &y2)) break; - if (x2 < x1 || x1 < 0 || x2 < 0 || x1 > 319 || x2 > 319) break; - if (y2 < y1 || y1 < 0 || y2 < 0 || y1 > 199 || y2 > 199) break; - outputByte(bytecode, PARSE_ELLIPSE); - outputWord(bytecode, x1); - outputWord(bytecode, y1); - outputWord(bytecode, x2); - outputWord(bytecode, y2); - lineOkay = TRUE; - break; - - case PARSE_FILL: - // Fill (value),(value) {to (value} - if (!parserGetXY(&tokenEnd, &x1, &y1)) break; - if (x1 < 0 || x1 > 319) break; - if (y1 < 0 || y1 > 199) break; - // Do they want to fill to a certain color? Or over the current color? - x2 = 16; // 16 == Fill, otherwise FillTo - if (parserGetWord("TO", &tokenEnd)) { - if (!parserGetX(&tokenEnd, &x2)) break; - if (x2 < 0 || x2 > 15) break; - } - outputByte(bytecode, PARSE_FILL); - outputWord(bytecode, x1); - outputWord(bytecode, y1); - outputByte(bytecode, x2); - lineOkay = TRUE; - break; - - case PARSE_LINE: - // Line (value),(value) to (value),(value) [to ...] - points = NULL; - if (!parserGetXY(&tokenEnd, &p1.x, &p1.y)) break; - if (!parserGetWord("TO", &tokenEnd)) break; - if (!parserGetXY(&tokenEnd, &p2.x, &p2.y)) break; - if (p1.x < 0 || p1.x > 319) break; - if (p1.y < 0 || p1.y > 199) break; - if (p2.x < 0 || p2.x > 319) break; - if (p2.y < 0 || p2.y > 199) break; - arrput(points, p1); - arrput(points, p2); - isOkay = TRUE; - while (parserGetWord("TO", &tokenEnd)) { - if (!parserGetXY(&tokenEnd, &p1.x, &p1.y)) { - // Error. Unwind array. - while (arrlen(points) > 0) { - arrdel(points, 0); - } - arrfree(points); - isOkay = FALSE; - break; - } - arrput(points, p1); - } - if (isOkay) { - outputByte(bytecode, PARSE_LINE); - outputWord(bytecode, arrlen(points)); - for (x1=0; x1 0) { - arrdel(points, 0); - } - arrfree(points); - lineOkay = TRUE; - } - break; - - case PARSE_PALETTE: - // Palette (short) AS (short),(short),(short) - if (!parserGetX(&tokenEnd, &x1)) break; - if (!parserGetWord("AS", &tokenEnd)) break; - if (!parserGetXYZ(&tokenEnd, &x2, &y1, &y2)) break; - if (x1 < 0 || x1 > 15) break; - if (x2 < 0 || x2 > 15) break; - if (y1 < 0 || y1 > 15) break; - if (y2 < 0 || y2 > 15) break; - outputByte(bytecode, PARSE_PALETTE); - outputByte(bytecode, x1); - outputByte(bytecode, x2); + // Find the operator. + token = strtok_r(NULL, " ", &tokenEnd); + if (token != NULL) { + keyword = 0; + while (math[keyword].operator) { + if (strcasecmp(math[keyword].operator, token) == 0) { + // Yep! Gather arguments and generate bytecode. + if (!parserGetX(&tokenEnd, variables, &x1)) break; + outputByte(bytecode, PARSE_MATH); outputByte(bytecode, y1); - outputByte(bytecode, y2); - lineOkay = TRUE; - break; - - case PARSE_PLOT: - // Plot (value),(value) - if (!parserGetXY(&tokenEnd, &x1, &y1)) break; - if (x1 < 0 || x1 > 319) break; - if (y1 < 0 || y1 > 199) break; - outputByte(bytecode, PARSE_PLOT); + outputByte(bytecode, math[keyword].id); outputWord(bytecode, x1); - outputWord(bytecode, y1); lineOkay = TRUE; break; + } + keyword++; + } + } - case PARSE_RECTANGLE: - // Rectangle (value),(value) to (value),(value) - if (!parserGetXY(&tokenEnd, &x1, &y1)) break; - if (!parserGetWord("TO", &tokenEnd)) break; - if (!parserGetXY(&tokenEnd, &x2, &y2)) break; - if (x2 < x1 || x1 < 0 || x2 < 0 || x1 > 319 || x2 > 319) break; - if (y2 < y1 || y1 < 0 || y2 < 0 || y1 > 199 || y2 > 199) break; - outputByte(bytecode, PARSE_RECTANGLE); - outputWord(bytecode, x1); - outputWord(bytecode, y1); - outputWord(bytecode, x2); - outputWord(bytecode, y2); - lineOkay = TRUE; + } else { // doing math + + // Is it a label? + if (strlen(token) > 1 && token[strlen(token) - 1] == ':') { + + // It's a label. + + } else { // doing a label + + // It's a keyword. + keyword = 0; + while (commands[keyword].command) { + if (strcasecmp(commands[keyword].command, token) == 0) { + + // Yep! Gather arguments and generate bytecode. + switch (commands[keyword].id) { + + case PARSE_NONE: + // Won't happen, but silences an error. + break; + + case PARSE_BOX: + // Box (value),(value) to (value),(value) + if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; + if (!parserGetWord("TO", &tokenEnd)) break; + if (!parserGetXY(&tokenEnd, variables, &x2, &y2)) break; + if (IS_NUMBER(x1) && IS_NUMBER(x2) && x2 < x1) break; + if (IS_NUMBER(x1) && (x1 < 0 || x1 > 319)) break; + if (IS_NUMBER(x2) && (x2 < 0 || x2 > 319)) break; + if (IS_NUMBER(y1) && IS_NUMBER(y2) && y2 < y1) break; + if (IS_NUMBER(y1) && (y1 < 0 || y1 > 199)) break; + if (IS_NUMBER(y2) && (y2 < 0 || y2 > 199)) break; + outputByte(bytecode, PARSE_BOX); + outputWord(bytecode, x1); + outputWord(bytecode, y1); + outputWord(bytecode, x2); + outputWord(bytecode, y2); + lineOkay = TRUE; + break; + + case PARSE_CIRCLE: + // Circle (value) at (value),(value) + if (!parserGetX(&tokenEnd, variables, &y2)) break; + if (!parserGetWord("AT", &tokenEnd)) break; + if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; + if (IS_NUMBER(x1) && (x1 < 0 || x1 > 319)) break; + if (IS_NUMBER(y1) && (y1 < 0 || y1 > 199)) break; + outputByte(bytecode, PARSE_CIRCLE); + outputWord(bytecode, y2); + outputWord(bytecode, x1); + outputWord(bytecode, y1); + lineOkay = TRUE; + break; + + case PARSE_CLEAR: + outputByte(bytecode, PARSE_CLEAR); + lineOkay = TRUE; + break; + + case PARSE_COLOR: + // Color (short) + if (!parserGetX(&tokenEnd, variables, &x1)) break; + if (IS_NUMBER(x1) && (x1 < 0 || x1 > 15)) break; + outputByte(bytecode, PARSE_COLOR); + outputByte(bytecode, x1); + lineOkay = TRUE; + break; + + case PARSE_COMMENT: + // Eat the rest of the line. + while (token != NULL) { + token = strtok_r(NULL, " ", &tokenEnd); + } + lineOkay = TRUE; + break; + + case PARSE_ELLIPSE: + // Ellipse (value),(value) to (value),(value) + if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; + if (!parserGetWord("TO", &tokenEnd)) break; + if (!parserGetXY(&tokenEnd, variables, &x2, &y2)) break; + if (IS_NUMBER(x1) && IS_NUMBER(x2) && x2 < x1) break; + if (IS_NUMBER(x1) && (x1 < 0 || x1 > 319)) break; + if (IS_NUMBER(x2) && (x2 < 0 || x2 > 319)) break; + if (IS_NUMBER(y1) && IS_NUMBER(y2) && y2 < y1) break; + if (IS_NUMBER(y1) && (y1 < 0 || y1 > 199)) break; + if (IS_NUMBER(y2) && (y2 < 0 || y2 > 199)) break; + outputByte(bytecode, PARSE_ELLIPSE); + outputWord(bytecode, x1); + outputWord(bytecode, y1); + outputWord(bytecode, x2); + outputWord(bytecode, y2); + lineOkay = TRUE; + break; + + case PARSE_FILL: + // Fill (value),(value) {to (value} + if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; + if (IS_NUMBER(x1) && (x1 < 0 || x1 > 319)) break; + if (IS_NUMBER(y1) && (y1 < 0 || y1 > 199)) break; + // Do they want to fill to a certain color? Or over the current color? + x2 = 16; // 16 == Fill, otherwise FillTo + if (parserGetWord("TO", &tokenEnd)) { + if (!parserGetX(&tokenEnd, variables, &x2)) break; + if (IS_NUMBER(x2) && (x2 < 0 || x2 > 15)) break; + } + outputByte(bytecode, PARSE_FILL); + outputWord(bytecode, x1); + outputWord(bytecode, y1); + outputByte(bytecode, x2); + lineOkay = TRUE; + break; + + case PARSE_LABEL: + // Won't happen. + break; + + case PARSE_LINE: + // Line (value),(value) to (value),(value) [to ...] + points = NULL; + if (!parserGetXY(&tokenEnd, variables, &p1.x, &p1.y)) break; + if (!parserGetWord("TO", &tokenEnd)) break; + if (!parserGetXY(&tokenEnd, variables, &p2.x, &p2.y)) break; + if (IS_NUMBER(p1.x) && (p1.x < 0 || p1.x > 319)) break; + if (IS_NUMBER(p1.y) && (p1.y < 0 || p1.y > 199)) break; + if (IS_NUMBER(p2.x) && (p2.x < 0 || p2.x > 319)) break; + if (IS_NUMBER(p2.y) && (p2.y < 0 || p2.y > 199)) break; + arrput(points, p1); + arrput(points, p2); + isOkay = TRUE; + while (parserGetWord("TO", &tokenEnd)) { + if (!parserGetXY(&tokenEnd, variables, &p1.x, &p1.y)) { + // Error. Unwind array. + while (arrlen(points) > 0) { + arrdel(points, 0); + } + arrfree(points); + isOkay = FALSE; + break; + } + arrput(points, p1); + } + if (isOkay) { + outputByte(bytecode, PARSE_LINE); + outputWord(bytecode, arrlen(points)); + for (x1 = 0; x1 < arrlen(points); x1++) { + outputWord(bytecode, points[x1].x); + outputWord(bytecode, points[x1].y); + } + // Unwind array. + while (arrlen(points) > 0) { + arrdel(points, 0); + } + arrfree(points); + lineOkay = TRUE; + } + break; + + case PARSE_MATH: + // Won't happen. + break; + + case PARSE_PALETTE: + // Palette (short) AS (short),(short),(short) + if (!parserGetX(&tokenEnd, variables, &x1)) break; + if (!parserGetWord("AS", &tokenEnd)) break; + if (!parserGetXYZ(&tokenEnd, variables, &x2, &y1, &y2)) break; + if (IS_NUMBER(x1) && (x1 < 0 || x1 > 15)) break; + if (IS_NUMBER(x2) && (x2 < 0 || x2 > 15)) break; + if (IS_NUMBER(y1) && (y1 < 0 || y1 > 15)) break; + if (IS_NUMBER(y2) && (y2 < 0 || y2 > 15)) break; + outputByte(bytecode, PARSE_PALETTE); + outputByte(bytecode, x1); + outputByte(bytecode, x2); + outputByte(bytecode, y1); + outputByte(bytecode, y2); + lineOkay = TRUE; + break; + + case PARSE_PLOT: + // Plot (value),(value) + if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; + if (IS_NUMBER(x1) && (x1 < 0 || x1 > 319)) break; + if (IS_NUMBER(y1) && (y1 < 0 || y1 > 199)) break; + outputByte(bytecode, PARSE_PLOT); + outputWord(bytecode, x1); + outputWord(bytecode, y1); + lineOkay = TRUE; + break; + + case PARSE_RECTANGLE: + // Rectangle (value),(value) to (value),(value) + if (!parserGetXY(&tokenEnd, variables, &x1, &y1)) break; + if (!parserGetWord("TO", &tokenEnd)) break; + if (!parserGetXY(&tokenEnd, variables, &x2, &y2)) break; + if (IS_NUMBER(x1) && IS_NUMBER(x2) && x2 < x1) break; + if (IS_NUMBER(x1) && (x1 < 0 || x1 > 319)) break; + if (IS_NUMBER(x2) && (x2 < 0 || x2 > 319)) break; + if (IS_NUMBER(y1) && IS_NUMBER(y2) && y2 < y1) break; + if (IS_NUMBER(y1) && (y1 < 0 || y1 > 199)) break; + if (IS_NUMBER(y2) && (y2 < 0 || y2 > 199)) break; + outputByte(bytecode, PARSE_RECTANGLE); + outputWord(bytecode, x1); + outputWord(bytecode, y1); + outputWord(bytecode, x2); + outputWord(bytecode, y2); + lineOkay = TRUE; + break; + + case PARSE_RESET: + outputByte(bytecode, PARSE_RESET); + lineOkay = TRUE; + break; + + } // switch + + // Stop looking for this keyword - we handled it. break; - case PARSE_RESET: - outputByte(bytecode, PARSE_RESET); - lineOkay = TRUE; - break; + } else { + // Keep looking until we find this keyword or run out of commands. + keyword++; + } // keyword match - } // switch + } // loop over commands - // Stop looking for this command - we handled it. - break; + } // label - } else { - // Keep looking until we find this command or run out of commands. - command++; - } // command match - - } // loop over commands + } // math // Is everything still okay? if (!lineOkay) { diff --git a/src/vector.c b/src/vector.c index 41841fb..c0501e8 100644 --- a/src/vector.c +++ b/src/vector.c @@ -27,7 +27,7 @@ #include "common.h" #include -#include +#include #include "scintillaHeaders.h" #include "gladeVector.h" #include "vector.h" @@ -59,23 +59,42 @@ typedef struct VectorDataS { cairo_surface_t *trace; jlContextT *jlc; double traceImagePercent; + float *variables; } VectorDataT; static int _nextEditorId = 0; +static int byte(VectorDataT *self, unsigned char byte); EVENT gboolean drawVectorImageDraw(GtkWidget *widget, cairo_t *cr, gpointer userData); EVENT void drawVectorImageClick(GtkWidget *object, GdkEventButton *event, gpointer userData); EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotification *notifyData, gpointer userData); EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData); +static int getWord(VecByteCodeT *bytecode, int *index); EVENT void menuVectorFileClose(GtkWidget *object, gpointer userData); static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self); EVENT void scaleVectorTraceImageValueChanged(GtkWidget *object, gpointer userData); +static float variable(VectorDataT *self, unsigned char byte); +static int word(VectorDataT *self, unsigned short word); EVENT gboolean winVectorClose(GtkWidget *object, gpointer userData); static void winVectorDelete(gpointer userData); +static int byte(VectorDataT *self, unsigned char byte) { + int value; + // Decode our weird byte format, look up variables, and return sane numbers. + // Is this a variable? + if (byte & 0x80) { + value = variable(self, byte); + } else { + // Bytes can't be negative in our world, so just return it. + value = byte; + } + return value; +} + + EVENT gboolean drawVectorImageDraw(GtkWidget *widget, cairo_t *cr, gpointer userData) { VectorDataT *self = (VectorDataT *)userData; int width = cairo_image_surface_get_width(self->surface); @@ -180,12 +199,12 @@ EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotifi SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_ARROW); SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_HIGHLIGHT); } else { - // All good! - renderBytecode(&byteCode, self); //***DEBUG*** FILE *out = fopen("bytecode.bin", "wb"); fwrite(byteCode.bytes, byteCode.length, 1, out); fclose(out); + // All good! + renderBytecode(&byteCode, self); } // Release bytecode. @@ -208,6 +227,18 @@ EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotifi } +static int getWord(VecByteCodeT *bytecode, int *index) { + int word; + + word = bytecode->bytes[*index] << 8; + *index = *index + 1; + word += bytecode->bytes[*index]; + *index = *index + 1; + + return word; +} + + EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData) { VectorDataT *self = (VectorDataT *)userData; char *filename; @@ -275,16 +306,20 @@ EVENT void menuVectorFileClose(GtkWidget *object, gpointer userData) { static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self) { - int index = 0; - int x1; - int y1; - int x2; - int y2; - int count; - int i; + int index = 0; + int x1; + int y1; + int x2; + int y2; + int count; + int i; + float f1; + float f2; #define GET_BYTE (bytecode->bytes[index++]) -#define GET_WORD ((bytecode->bytes[index++] << 8) + bytecode->bytes[index++]) +#define GET_WORD getWord(bytecode, &index) + + self->variables = NULL; while (index < bytecode->length) { switch (bytecode->bytes[index++]) { @@ -294,18 +329,18 @@ static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self) { break; case PARSE_BOX: - x1 = GET_WORD; - y1 = GET_WORD; - x2 = GET_WORD; - y2 = GET_WORD; + x1 = word(self, GET_WORD); + y1 = word(self, GET_WORD); + x2 = word(self, GET_WORD); + y2 = word(self, GET_WORD); printf("Box %d,%d to %d,%d\n", x1, y1, x2, y2); jlDrawBox(self->jlc, x1, y1, x2, y2); break; case PARSE_CIRCLE: - y2 = GET_WORD; - x1 = GET_WORD; - y1 = GET_WORD; + y2 = word(self, GET_WORD); + x1 = word(self, GET_WORD); + y1 = word(self, GET_WORD); printf("Circle %d at %d,%d\n", y2, x1, y1); jlDrawCircle(self->jlc, x1, y1, y2); break; @@ -316,28 +351,28 @@ static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self) { break; case PARSE_COLOR: - x1 = GET_BYTE; + x1 = byte(self, GET_BYTE); printf("Color %d\n", x1); jlDrawColorSet(self->jlc, x1); break; case PARSE_COMMENT: - // Nothing to do! + // Won't happen. Nothing to do! break; case PARSE_ELLIPSE: - x1 = GET_WORD; - y1 = GET_WORD; - x2 = GET_WORD; - y2 = GET_WORD; + x1 = word(self, GET_WORD); + y1 = word(self, GET_WORD); + x2 = word(self, GET_WORD); + y2 = word(self, GET_WORD); printf("Ellipse %d,%d to %d,%d\n", x1, y1, x2, y2); jlDrawEllipse(self->jlc, x1, y1, x2, y2); break; case PARSE_FILL: - x1 = GET_WORD; - y1 = GET_WORD; - x2 = GET_BYTE; + x1 = word(self, GET_WORD); + y1 = word(self, GET_WORD); + x2 = byte(self, GET_BYTE); if (x2 > 15) { printf("Fill %d,%d\n", x1, y1); jlDrawFill(self->jlc, x1, y1); @@ -347,14 +382,17 @@ static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self) { } break; + case PARSE_LABEL: + break; + case PARSE_LINE: - count = GET_WORD; - x1 = GET_WORD; - y1 = GET_WORD; + count = word(self, GET_WORD); + x1 = word(self, GET_WORD); + y1 = word(self, GET_WORD); printf("Line %d,%d", x1, y1); for (i=0; ijlc, x1, y1, x2, y2); x1 = x2; @@ -363,27 +401,97 @@ static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self) { printf("\n"); break; + case PARSE_MATH: + x1 = GET_BYTE; // var index + y1 = GET_BYTE; // operand + x2 = GET_WORD; // Possible variable + if (x2 & 0x8000) { + f2 = variable(self, x2); + } else { + f2 = x2; + } + f1 = variable(self, x1); + switch (y1) { + case MATH_NONE: + // Well, none! + break; + + case MATH_ASSIGN: + f1 = f2; + break; + + case MATH_ADD: + f1 += f2; + break; + + case MATH_SUBTRACT: + f1 -= f2; + break; + + case MATH_MULTIPLY: + f1 *= f2; + break; + + case MATH_DIVIDE: + f1 /= f2; + break; + + case MATH_MOD: + f1 = modff(f1, &f2); + break; + + case MATH_POW: + f1 = powf(f1, f2); + break; + + case MATH_SQRT: + f1 = sqrtf(f2); + break; + + case MATH_ABS: + f1 = fabsf(f2); + break; + + case MATH_COS: + f1 = cosf(f2); + break; + + case MATH_SIN: + f1 = sinf(f2); + break; + + case MATH_TAN: + f1 = tanf(f2); + break; + } + // Make sure we have enough slots for this variable. + while (arrlen(self->variables) <= x1) { + arrput(self->variables, 0.0f); + } + self->variables[x1] = f1; + break; + case PARSE_PALETTE: - x1 = GET_BYTE; - x2 = GET_BYTE; - y1 = GET_BYTE; - y2 = GET_BYTE; + x1 = byte(self, GET_BYTE); + x2 = byte(self, GET_BYTE); + y1 = byte(self, GET_BYTE); + y2 = byte(self, GET_BYTE); printf("Palette %d as %d,%d,%d\n", x1, x2, y1, y2); jlPaletteSet(self->jlc, x1, x2, y1, y2); break; case PARSE_PLOT: - x1 = GET_WORD; - y1 = GET_WORD; + x1 = word(self, GET_WORD); + y1 = word(self, GET_WORD); printf("Plot %d,%d\n", x1, y1); jlDrawPixelSet(self->jlc, x1, y1); break; case PARSE_RECTANGLE: - x1 = GET_WORD; - y1 = GET_WORD; - x2 = GET_WORD; - y2 = GET_WORD; + x1 = word(self, GET_WORD); + y1 = word(self, GET_WORD); + x2 = word(self, GET_WORD); + y2 = word(self, GET_WORD); printf("Rectangle %d,%d to %d,%d\n", x1, y1, x2, y2); jlDrawBoxFilled(self->jlc, x1, y1, x2, y2); break; @@ -399,6 +507,12 @@ static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self) { } // switch } // while + // Clear variables. + while (arrlen(self->variables) > 0) { + arrdel(self->variables, 0); + } + arrfree(self->variables); + // Refresh widget. gtk_widget_queue_draw(self->drawVectorImage); } @@ -416,6 +530,43 @@ EVENT void scaleVectorTraceImageValueChanged(GtkWidget *object, gpointer userDat } +static float variable(VectorDataT *self, unsigned char byte) { + int value; + + // Clear possibly set variable flag. + byte &= 0xbf; + + // Look up variable and return value. + if (byte < arrlen(self->variables)) { + value = self->variables[byte]; + } else { + // We don't know this variable yet. Be like BASIC. + value = 0; + } + return value; +} + + +static int word(VectorDataT *self, unsigned short word) { + int value; + // Decode our weird word format, look up variables, and return sane numbers. + // Is this a variable? + if (word & 0x8000) { + value = variable(self, word); + } else { + // Is it negative? + if (word & 0x4000) { + // Clear the negative flag and actually negate it. + value = 0 - (word & 0xbfff); + } else { + // Just a boring number, copy it out. + value = word; + } + } + return value; +} + + EVENT gboolean winVectorClose(GtkWidget *object, gpointer userData) { // userData is not reliable due to menuVectorFileClose and util indirectly calling us. VectorDataT *self = (VectorDataT *)utilGetWindowData(object); @@ -511,10 +662,13 @@ void winVectorCreate(void) { // Debug SSM(SCI_INSERTTEXT, 0, (sptr_t) "reset\n" - "palette 15 as 15,0,0\n" - "color 15\n" + "%red = 15\n" + "palette %red as 15,0,0\n" + "color %red\n" "box 0,0 to 319,199\n" - "color 14\n" + "%yellow = %red\n" + "%yellow - 1\n" + "color %yellow\n" "line 27,22 to 289,24 to 297,169 to 27,166 to 27,23\n" "color 11\n" "circle 50 at 159,95\n"