|
|
| Line 1: |
Line 1: |
| - | = BASIC Tokeniser/Detokeniser = | + | = Read the Joystick = |
| | | | |
| - | Some C++ (but trivial to convert into C) routines that convert to/from a memory dump of any Acorn machine running the BASIC II ROM and an ASCII file of the BASIC program. Originally taken from my ElectrEm emulator.
| + | Uses OSBYTE &80: |
| | | | |
| - | Routines of interest are ''bool ImportBASIC(char *Filename, Uint8 *Mem)'' and ''bool ExportBASIC(char *Filename, Uint8 *Memory)''. ''SetupBASICTables()'' must be called once before either routine is used.
| + | X = 0 Read Fire Buttons in X |
| | | | |
| - | == The Code == | + | X = 1 Read X Axis in Y;X (Returns 0- 65535 Right<->Left) |
| | | | |
| - | <pre>/* | + | X = 2 Read Y Axis in Y;X (returns 0- 65535 Down<->Up) |
| | | | |
| - | BASIC.cpp | |
| - | ========= | |
| | | | |
| - | This is the code to take an Electron memory dump and then save
| + | == The Code == |
| - | the current BASIC file to an ASCII file, or alternatively to
| + | |
| - | open an ASCII file, tokenise it and put it into memory
| + | |
| - | | + | |
| - | */
| + | |
| - | | + | |
| - | #include "BASIC.h"
| + | |
| - | #include <stdio.h>
| + | |
| - | | + | |
| - | /*
| + | |
| - | | + | |
| - | KeyWord table - used by both the exporter and the importer to convert
| + | |
| - | between the plain text version of a BASIC program and the format used
| + | |
| - | internally by the BASIC ROM
| + | |
| - | | + | |
| - | Information taken from pages 41-43 of the BASIC ROM User Guide,
| + | |
| - | BASIC 2 ROM assumed throughout
| + | |
| - | | + | |
| - | */
| + | |
| - | | + | |
| - | struct KeyWord
| + | |
| - | {
| + | |
| - | char *Name;
| + | |
| - | Uint8 Flags;
| + | |
| - | unsigned int StrLen;
| + | |
| - | struct KeyWord *Next;
| + | |
| - | };
| + | |
| - | | + | |
| - | struct KeyWord KeyWordTable[0x80] =
| + | |
| - | {
| + | |
| - | /* 0x80 */
| + | |
| - | {"AND", 0x00}, {"DIV", 0x00}, {"EOR", 0x00}, {"MOD", 0x00},
| + | |
| - | {"OR", 0x00}, {"ERROR", 0x04}, {"LINE", 0x00}, {"OFF", 0x00},
| + | |
| - | | + | |
| - | /* 0x88 */
| + | |
| - | {"STEP", 0x00}, {"SPC", 0x00}, {"TAB(", 0x00}, {"ELSE", 0x14},
| + | |
| - | {"THEN", 0x14}, {"", 0x00}, {"OPENIN",0x00}, {"PTR", 0x43},
| + | |
| - | | + | |
| - | /* 0x90 */
| + | |
| - | {"PAGE", 0x43}, {"TIME", 0x43}, {"LOMEM", 0x43}, {"HIMEM", 0x43},
| + | |
| - | {"ABS", 0x00}, {"ACS", 0x00}, {"ADVAL", 0x00}, {"ASC", 0x00},
| + | |
| - | | + | |
| - | /* 0x98 */
| + | |
| - | {"ASN", 0x00}, {"ATN", 0x00}, {"BGET", 0x01}, {"COS", 0x00},
| + | |
| - | {"COUNT", 0x01}, {"DEG", 0x00}, {"ERL", 0x01}, {"ERR", 0x01},
| + | |
| - | | + | |
| - | /* 0xa0 */
| + | |
| - | {"EVAL", 0x00}, {"EXP", 0x00}, {"EXT", 0x01}, {"FALSE", 0x01},
| + | |
| - | {"FN", 0x08}, {"GET", 0x00}, {"INKEY", 0x00}, {"INSTR(", 0x00},
| + | |
| - | | + | |
| - | /* 0xa8 */
| + | |
| - | {"INT", 0x00}, {"LEN", 0x00}, {"LN", 0x00}, {"LOG", 0x00},
| + | |
| - | {"NOT", 0x00}, {"OPENUP",0x00}, {"OPENOUT",0x00}, {"PI", 0x01},
| + | |
| - | | + | |
| - | /* 0xb0 */
| + | |
| - | {"POINT(", 0x00}, {"POS", 0x01}, {"RAD", 0x00}, {"RND", 0x01},
| + | |
| - | {"SGN", 0x00}, {"SIN", 0x00}, {"SQR", 0x00}, {"TAN", 0x00},
| + | |
| - | | + | |
| - | /* 0xb8 */
| + | |
| - | {"TO", 0x00}, {"TRUE", 0x01}, {"USR", 0x00}, {"VAL", 0x00},
| + | |
| - | {"VPOS", 0x01}, {"CHR$", 0x00}, {"GET$", 0x00}, {"INKEY$", 0x00},
| + | |
| - | | + | |
| - | /* 0xc0 */
| + | |
| - | {"LEFT$(", 0x00}, {"MID$(", 0x00}, {"RIGHT$(",0x00}, {"STR$", 0x00},
| + | |
| - | {"STRING$(",0x00}, {"EOF", 0x01}, {"AUTO", 0x10}, {"DELETE", 0x10},
| + | |
| - | | + | |
| - | /* 0xc8 */
| + | |
| - | {"LOAD", 0x02}, {"LIST", 0x10}, {"NEW", 0x01}, {"OLD", 0x01},
| + | |
| - | {"RENUMBER",0x10}, {"SAVE", 0x02}, {"", 0x00}, {"PTR", 0x00},
| + | |
| - | | + | |
| - | /* 0xd0 */
| + | |
| - | {"PAGE", 0x00}, {"TIME", 0x01}, {"LOMEM", 0x00}, {"HIMEM", 0x00},
| + | |
| - | {"SOUND", 0x02}, {"BPUT", 0x03}, {"CALL", 0x02}, {"CHAIN", 0x02},
| + | |
| - | | + | |
| - | /* 0xd8 */
| + | |
| - | {"CLEAR", 0x01}, {"CLOSE", 0x03}, {"CLG", 0x01}, {"CLS", 0x01},
| + | |
| - | {"DATA", 0x20}, {"DEF", 0x00}, {"DIM", 0x02}, {"DRAW", 0x02},
| + | |
| - | | + | |
| - | /* 0xe0 */
| + | |
| - | {"END", 0x01}, {"ENDPROC",0x01}, {"ENVELOPE",0x02}, {"FOR", 0x02},
| + | |
| - | {"GOSUB", 0x12}, {"GOTO", 0x12}, {"GCOL", 0x02}, {"IF", 0x02},
| + | |
| - | | + | |
| - | /* 0xe8 */
| + | |
| - | {"INPUT", 0x02}, {"LET", 0x04}, {"LOCAL", 0x02}, {"MODE", 0x02},
| + | |
| - | {"MOVE", 0x02}, {"NEXT", 0x02}, {"ON", 0x02}, {"VDU", 0x02},
| + | |
| - | | + | |
| - | /* 0xf0 */
| + | |
| - | {"PLOT", 0x02}, {"PRINT", 0x02}, {"PROC", 0x0a}, {"READ", 0x02},
| + | |
| - | {"REM", 0x20}, {"REPEAT",0x00}, {"REPORT",0x01}, {"RESTORE",0x12},
| + | |
| - | | + | |
| - | /* 0xf8 */
| + | |
| - | {"RETURN", 0x01}, {"RUN", 0x01}, {"STOP", 0x01}, {"COLOUR", 0x02},
| + | |
| - | {"TRACE", 0x12}, {"UNTIL", 0x02}, {"WIDTH", 0x02}, {"OSCLI", 0x02}
| + | |
| - | };
| + | |
| - | | + | |
| - | KeyWord *QuickTable[26*26];
| + | |
| - | | + | |
| - | /*
| + | |
| - | | + | |
| - | Setup function, to establish contents of QuickTable, store strlens, etc
| + | |
| - | | + | |
| - | */
| + | |
| - | | + | |
| - | #define HashCode(str) (str[0] < 'A' || str[0] > 'Z' || str[1] < 'A' || str[1] > 'Z') ? 0 : ((str[0] - 'A')*26 + (str[1] - 'A'))
| + | |
| - | | + | |
| - | void SetupBASICTables()
| + | |
| - | {
| + | |
| - | /* set QuickTable to empty */
| + | |
| - | int c = 26*26;
| + | |
| - | while(c--)
| + | |
| - | QuickTable[c] = NULL;
| + | |
| - | | + | |
| - | /* go through tokens, store strlens & populate QuickTable */
| + | |
| - | for(c = 0; c < 0x80; c++)
| + | |
| - | {
| + | |
| - | if(KeyWordTable[c].StrLen = strlen(KeyWordTable[c].Name))
| + | |
| - | {
| + | |
| - | /* reject any symbols that have already appeared 0x40 places earlier in the table */
| + | |
| - | if(c < 0x40 || strcmp(KeyWordTable[c].Name, KeyWordTable[c - 0x40].Name))
| + | |
| - | {
| + | |
| - | int Code = HashCode(KeyWordTable[c].Name);
| + | |
| - | KeyWord **InsertPointer = &QuickTable[Code];
| + | |
| - | while(*InsertPointer)
| + | |
| - | InsertPointer = &(*InsertPointer)->Next;
| + | |
| - | | + | |
| - | *InsertPointer = &KeyWordTable[c];
| + | |
| - | }
| + | |
| - | }
| + | |
| - | }
| + | |
| - | | + | |
| - | /*
| + | |
| - | | + | |
| - | Go through QuickTable, sorting each branch by string length
| + | |
| - | | + | |
| - | I'm an idiot, so I've used insertion sort!
| + | |
| - | | + | |
| - | */
| + | |
| - | c = 26*26;
| + | |
| - | while(c--)
| + | |
| - | if(QuickTable[c] && QuickTable[c]->Next)
| + | |
| - | {
| + | |
| - | /* sort first by string length */
| + | |
| - | KeyWord **Check = &QuickTable[c];
| + | |
| - | unsigned int CurLength = (*Check)->StrLen;
| + | |
| - | Check = &(*Check)->Next;
| + | |
| - | while(*Check)
| + | |
| - | {
| + | |
| - | /* check if out of order */
| + | |
| - | if((*Check)->StrLen > CurLength)
| + | |
| - | {
| + | |
| - | /* unlink */
| + | |
| - | KeyWord *Takeout = *Check;
| + | |
| - | *Check = (*Check)->Next;
| + | |
| - | | + | |
| - | /* start at top of list, find correct insertion point */
| + | |
| - | KeyWord **InsertPoint = &QuickTable[c];
| + | |
| - | while((*InsertPoint)->StrLen >= Takeout->StrLen)
| + | |
| - | InsertPoint = &(*InsertPoint)->Next;
| + | |
| - | | + | |
| - | /* ...and insert */
| + | |
| - | Takeout->Next = *InsertPoint;
| + | |
| - | *InsertPoint = Takeout;
| + | |
| - | }
| + | |
| - | else
| + | |
| - | {
| + | |
| - | CurLength = (*Check)->StrLen;
| + | |
| - | Check = &(*Check)->Next;
| + | |
| - | }
| + | |
| - | }
| + | |
| - | }
| + | |
| - | }
| + | |
| - | | + | |
| - | /*
| + | |
| - | | + | |
| - | Little function to return an error string
| + | |
| - | | + | |
| - | */
| + | |
| - | char *ErrorTable[] =
| + | |
| - | {
| + | |
| - | "",
| + | |
| - | "BASIC is not currently active",
| + | |
| - | "Unable to open file for input",
| + | |
| - | "Program too large",
| + | |
| - | "Unable to open file for output",
| + | |
| - | "Malformed BASIC program or not running BASIC",
| + | |
| - | "BASIC program appears to run past the end of RAM"
| + | |
| - | };
| + | |
| - | char DynamicErrorText[256];
| + | |
| - | | + | |
| - | int ErrorNum;
| + | |
| - | | + | |
| - | char *GetBASICError()
| + | |
| - | {
| + | |
| - | return ErrorNum >= 0 ? ErrorTable[ErrorNum] : DynamicErrorText;
| + | |
| - | }
| + | |
| - | | + | |
| - | /*
| + | |
| - | | + | |
| - | Functions to export BASIC code, i.e. decode from tokenised form to plain text
| + | |
| - | | + | |
| - | */
| + | |
| - | | + | |
| - | bool ExtractLine(FILE *output, Uint8 *Memory, Uint16 Addr, Uint8 LineL)
| + | |
| - | {
| + | |
| - | int LineLength = (int)LineL;
| + | |
| - | | + | |
| - | while(LineLength >= 0)
| + | |
| - | {
| + | |
| - | Uint8 ThisByte = Memory[Addr]; Addr++; LineLength--;
| + | |
| - | if(ThisByte >= 0x80)
| + | |
| - | {
| + | |
| - | if(ThisByte == 0x8d) // then we're about to see a tokenised line number
| + | |
| - | {
| + | |
| - | Uint16 LineNumber;
| + | |
| - | | + | |
| - | // decode weirdo tokenised line number format
| + | |
| - | LineNumber = Memory[Addr+1]&0x3f;
| + | |
| - | LineNumber |= (Memory[Addr+2]&0x3f) << 8;
| + | |
| - | LineNumber |= (Memory[Addr]&0x0c) << 12;
| + | |
| - | LineNumber |= (Memory[Addr]&0x30) << 2;
| + | |
| - | LineNumber ^= 0x4040;
| + | |
| - | | + | |
| - | Addr += 3;
| + | |
| - | LineLength -= 3;
| + | |
| - | | + | |
| - | fprintf(output, "%d", LineNumber);
| + | |
| - | }
| + | |
| - | else //ordinary keyword
| + | |
| - | {
| + | |
| - | fputs(KeyWordTable[ThisByte - 0x80].Name, output);
| + | |
| - |
| + | |
| - | if(KeyWordTable[ThisByte - 0x80].Flags & 0x20)
| + | |
| - | {
| + | |
| - | //copy to end of line without interpreting tokens}
| + | |
| - | while(LineLength >= 0)
| + | |
| - | {
| + | |
| - | fputc(Memory[Addr], output); Addr++; LineLength--;
| + | |
| - | }
| + | |
| - | return true;
| + | |
| - | }
| + | |
| - | }
| + | |
| - | }
| + | |
| - | else
| + | |
| - | {
| + | |
| - | switch(ThisByte)
| + | |
| - | {
| + | |
| - | default: fputc(ThisByte, output); break;
| + | |
| - | case '"':
| + | |
| - | /* copy string literal... */
| + | |
| - | fputc('"', output);
| + | |
| - | while(Memory[Addr] != '"' && LineLength >= 0)
| + | |
| - | {
| + | |
| - | fputc(Memory[Addr], output);
| + | |
| - | Addr++; LineLength--;
| + | |
| - | }
| + | |
| - | if(Memory[Addr] == '"')
| + | |
| - | {
| + | |
| - | fputc(Memory[Addr], output);
| + | |
| - | Addr++; LineLength--;
| + | |
| - | }
| + | |
| - | break;
| + | |
| - | }
| + | |
| - | }
| + | |
| - | }
| + | |
| - | | + | |
| - | return (LineLength == -1) ? true : false;
| + | |
| - | }
| + | |
| - | | + | |
| - | bool ExportBASIC(char *Filename, Uint8 *Memory)
| + | |
| - | {
| + | |
| - | ErrorNum = 0;
| + | |
| - | FILE *output = fopen(Filename, "wt");
| + | |
| - | | + | |
| - | if(!output)
| + | |
| - | {
| + | |
| - | ErrorNum = 4;
| + | |
| - | return false;
| + | |
| - | }
| + | |
| - | | + | |
| - | /* get the value of PAGE‚ start reading BASIC code from there */
| + | |
| - | Uint16 Addr = Memory[0x18] << 8;
| + | |
| - | | + | |
| - | if(Addr >= 32768 - 4)
| + | |
| - | ErrorNum = 6;
| + | |
| - | | + | |
| - | while(!ErrorNum)
| + | |
| - | {
| + | |
| - | /* character here should be \r */
| + | |
| - | if(Memory[Addr] != 0x0d)
| + | |
| - | {
| + | |
| - | ErrorNum = 5;
| + | |
| - | break;
| + | |
| - | }
| + | |
| - | Addr++;
| + | |
| - | | + | |
| - | /* get line number, check if we've hit the end of BASIC */
| + | |
| - | Uint16 LineNumber;
| + | |
| - | LineNumber = (Memory[Addr] << 8) | Memory[Addr+1];
| + | |
| - | Addr += 2;
| + | |
| - | if(LineNumber & 0x8000) // if we've hit the end of the program, exit
| + | |
| - | break;
| + | |
| - | | + | |
| - | Uint8 LineLength = Memory[Addr]; Addr++;
| + | |
| - | | + | |
| - | if(Addr+LineLength >= 32768 - 4)
| + | |
| - | {
| + | |
| - | ErrorNum = 6;
| + | |
| - | break;
| + | |
| - | }
| + | |
| - | | + | |
| - | /* print line number */
| + | |
| - | fprintf(output, "%5d", LineNumber);
| + | |
| - | | + | |
| - | /* detokenise, etc */
| + | |
| - | if(!ExtractLine(output, Memory, Addr, LineLength - 4))
| + | |
| - | break;
| + | |
| - | | + | |
| - | /* add a newline */
| + | |
| - | fputc('\n', output);
| + | |
| - | | + | |
| - | /* should process line here, but chicken out */
| + | |
| - | Addr += LineLength - 4;
| + | |
| - | }
| + | |
| - | | + | |
| - | fclose(output);
| + | |
| - | | + | |
| - | return ErrorNum ? false : true;
| + | |
| - | }
| + | |
| - | | + | |
| - | /*
| + | |
| - | | + | |
| - | Functions to import BASIC code, i.e. tokenise from plain text
| + | |
| - | | + | |
| - | */
| + | |
| - | | + | |
| - | #define AlphaNumeric(v)\
| + | |
| - | (\
| + | |
| - | (v >= 'a' && v <= 'z') ||\
| + | |
| - | (v >= 'A' && v <= 'Z') ||\
| + | |
| - | (v >= '0' && v <= '9')\
| + | |
| - | )
| + | |
| - | | + | |
| - | char IncomingBuffer[9];
| + | |
| - | Uint8 Token, NextChar;
| + | |
| - | unsigned int IncomingPointer;
| + | |
| - | FILE *inputfile;
| + | |
| - | bool EndOfFile, NumberStart;
| + | |
| - | unsigned int NumberValue, NumberLength;
| + | |
| - | int CurLine;
| + | |
| - | | + | |
| - | Uint8 *Memory;
| + | |
| - | Uint16 Addr;
| + | |
| - | | + | |
| - | inline bool WriteByte(Uint8 value)
| + | |
| - | {
| + | |
| - | if(Addr == 32768) {ErrorNum = 3; return false;}
| + | |
| - | Memory[Addr++] = value;
| + | |
| - | return true;
| + | |
| - | }
| + | |
| - | | + | |
| - | int my_fgetc(FILE *in)
| + | |
| - | {
| + | |
| - | int r;
| + | |
| - | do
| + | |
| - | {
| + | |
| - | r = fgetc(in);
| + | |
| - | }
| + | |
| - | while(r == '\r');
| + | |
| - | if(r == '\n') CurLine++;
| + | |
| - | return r;
| + | |
| - | }
| + | |
| - | | + | |
| - | void GetCharacter()
| + | |
| - | {
| + | |
| - | if(IncomingPointer == 8)
| + | |
| - | {
| + | |
| - | /* shift, load into position [8] */
| + | |
| - | int c = 0;
| + | |
| - | while(c < 8)
| + | |
| - | {
| + | |
| - | IncomingBuffer[c] = IncomingBuffer[c+1];
| + | |
| - | c++;
| + | |
| - | }
| + | |
| - | | + | |
| - | IncomingBuffer[8] = my_fgetc(inputfile);
| + | |
| - | }
| + | |
| - | else
| + | |
| - | {
| + | |
| - | IncomingBuffer[IncomingPointer] = my_fgetc(inputfile);
| + | |
| - | IncomingPointer++;
| + | |
| - | }
| + | |
| - | | + | |
| - | if(feof(inputfile)) //if we've hit feof then the last char isn't anything
| + | |
| - | {
| + | |
| - | IncomingPointer--;
| + | |
| - | if(!IncomingPointer)
| + | |
| - | {
| + | |
| - | EndOfFile = true;
| + | |
| - | return;
| + | |
| - | }
| + | |
| - | }
| + | |
| - | | + | |
| - | /* check for tokens, set flags accordingly. Be a bit dense about this for now! */
| + | |
| - | Token = IncomingBuffer[0];
| + | |
| - | int Code = HashCode(IncomingBuffer);
| + | |
| - | KeyWord *CheckPtr = QuickTable[Code];
| + | |
| - | | + | |
| - | while(CheckPtr)
| + | |
| - | {
| + | |
| - | if(IncomingPointer >= CheckPtr->StrLen && !strncmp(IncomingBuffer, CheckPtr->Name, CheckPtr->StrLen))
| + | |
| - | {
| + | |
| - | Token = (CheckPtr - KeyWordTable) + 0x80;
| + | |
| - | NextChar = IncomingBuffer[CheckPtr->StrLen];
| + | |
| - | break;
| + | |
| - | }
| + | |
| - | | + | |
| - | CheckPtr = CheckPtr->Next;
| + | |
| - | }
| + | |
| - | | + | |
| - | /* check if this is a number start */
| + | |
| - | NumberStart = false;
| + | |
| - | if(Token >= '0' && Token <= '9')
| + | |
| - | {
| + | |
| - | NumberStart = true;
| + | |
| - | char *end;
| + | |
| - | NumberValue = strtol(IncomingBuffer, &end, 10);
| + | |
| - | NumberLength = end - IncomingBuffer;
| + | |
| - | }
| + | |
| - | }
| + | |
| - | | + | |
| - | void EatCharacters(int n)
| + | |
| - | {
| + | |
| - | /* shift left n places, decrease IncomingPointer */
| + | |
| - | int c = 0;
| + | |
| - | while(c < 9-n)
| + | |
| - | {
| + | |
| - | IncomingBuffer[c] = IncomingBuffer[c+n];
| + | |
| - | c++;
| + | |
| - | }
| + | |
| - | IncomingPointer -= n;
| + | |
| - | IncomingBuffer[IncomingPointer] = '\0';
| + | |
| - | while(n--) /* this is a quick fix: it causes lots of unnecessary token searches... */
| + | |
| - | GetCharacter();
| + | |
| - | }
| + | |
| - | | + | |
| - | bool CopyStringLiteral()
| + | |
| - | {
| + | |
| - | // eat preceeding quote
| + | |
| - | WriteByte(IncomingBuffer[0]);
| + | |
| - | EatCharacters(1);
| + | |
| - | | + | |
| - | // don't tokenise anything until another quote is hit, keep eye out for things that may have gone wrong
| + | |
| - | while(!ErrorNum && !EndOfFile && IncomingBuffer[0] != '"' && IncomingBuffer[0] != '\n')
| + | |
| - | {
| + | |
| - | WriteByte(IncomingBuffer[0]);
| + | |
| - | EatCharacters(1);
| + | |
| - | }
| + | |
| - | | + | |
| - | if(IncomingBuffer[0] != '"') // stopped going for some reason other than a close quote
| + | |
| - | {
| + | |
| - | ErrorNum = -1;
| + | |
| - | sprintf(DynamicErrorText, "Malformed string literal on line %d", CurLine);
| + | |
| - | return false;
| + | |
| - | }
| + | |
| - | | + | |
| - | // eat proceeding quote
| + | |
| - | WriteByte(IncomingBuffer[0]);
| + | |
| - | EatCharacters(1);
| + | |
| - | | + | |
| - | return true;
| + | |
| - | }
| + | |
| - | | + | |
| - | bool DoLineNumberTokeniser()
| + | |
| - | {
| + | |
| - | while(!ErrorNum && !EndOfFile)
| + | |
| - | {
| + | |
| - | if(NumberStart)
| + | |
| - | {
| + | |
| - | // tokenise line number
| + | |
| - | Uint16 LineNumber = NumberValue ^ 0x4040;
| + | |
| - | | + | |
| - | WriteByte(0x8d);
| + | |
| - | | + | |
| - | WriteByte(((LineNumber&0xc0) >> 2) | ((LineNumber&0xc000) >> 12) | 0x40);
| + | |
| - | WriteByte((LineNumber&0x3f) | 0x40);
| + | |
| - | WriteByte(((LineNumber >> 8)&0x3f) | 0x40);
| + | |
| - | | + | |
| - | EatCharacters(NumberLength);
| + | |
| - | }
| + | |
| - | else
| + | |
| - | switch(Token)
| + | |
| - | {
| + | |
| - | // whitespace and commas do not cause this mode to exit
| + | |
| - | case ' ':
| + | |
| - | case ',':
| + | |
| - | WriteByte(Token);
| + | |
| - | EatCharacters(1);
| + | |
| - | break;
| + | |
| - | | + | |
| - | // hex numbers get through unscathed too
| + | |
| - | case '&':
| + | |
| - | WriteByte(Token);
| + | |
| - | EatCharacters(1);
| + | |
| - | | + | |
| - | while(
| + | |
| - | !ErrorNum &&
| + | |
| - | !EndOfFile &&
| + | |
| - | (
| + | |
| - | (IncomingBuffer[0] >= '0' && IncomingBuffer[0] <= '9') ||
| + | |
| - | (IncomingBuffer[0] >= 'A' && IncomingBuffer[0] <= 'F')
| + | |
| - | )
| + | |
| - | )
| + | |
| - | {
| + | |
| - | WriteByte(IncomingBuffer[0]);
| + | |
| - | EatCharacters(1);
| + | |
| - | }
| + | |
| - | break;
| + | |
| - | | + | |
| - | /* grab strings without tokenising numbers */
| + | |
| - | case '"':
| + | |
| - | if(!CopyStringLiteral())
| + | |
| - | return false;
| + | |
| - | break;
| + | |
| - | | + | |
| - | /* default action is to turn off line number tokenising and get back to normal */
| + | |
| - | default: return true;
| + | |
| - | }
| + | |
| - | }
| + | |
| - | return true;
| + | |
| - | }
| + | |
| - | | + | |
| - | bool EncodeLine()
| + | |
| - | {
| + | |
| - | bool StartOfStatement = true;
| + | |
| - | | + | |
| - | /* continue until we hit a '\n' or file ends */
| + | |
| - | while(!EndOfFile && Token != '\n' && !ErrorNum)
| + | |
| - | {
| + | |
| - | /* even if this looks like a keyword, it really isn't if the conditional flag is set & the next char is alphanumeric*/
| + | |
| - | if(
| + | |
| - | Token >= 0x80 &&
| + | |
| - | (KeyWordTable[Token - 0x80].Flags&1) &&
| + | |
| - | AlphaNumeric(NextChar)
| + | |
| - | )
| + | |
| - | Token = IncomingBuffer[0];
| + | |
| - | | + | |
| - | if(Token < 0x80) //if not a keyword token
| + | |
| - | {
| + | |
| - | switch(Token)
| + | |
| - | {
| + | |
| - | default: //default is dump character to memory
| + | |
| - | WriteByte(Token);
| + | |
| - | | + | |
| - | if(Token == ':') // a colon always switches the tokeniser back to "start of statement" mode
| + | |
| - | StartOfStatement = true;
| + | |
| - | | + | |
| - | // grab entire variables rather than allowing bits to be tokenised
| + | |
| - | if
| + | |
| - | (
| + | |
| - | (Token >= 'a' && Token <= 'z') ||
| + | |
| - | (Token >= 'A' && Token <= 'Z')
| + | |
| - | )
| + | |
| - | {
| + | |
| - | StartOfStatement = false;
| + | |
| - | EatCharacters(1);
| + | |
| - | while(AlphaNumeric(IncomingBuffer[0]))
| + | |
| - | {
| + | |
| - | WriteByte(IncomingBuffer[0]);
| + | |
| - | EatCharacters(1);
| + | |
| - | }
| + | |
| - | }
| + | |
| - | else
| + | |
| - | EatCharacters(1);
| + | |
| - | | + | |
| - | break;
| + | |
| - | case '*':
| + | |
| - | WriteByte(Token);
| + | |
| - | EatCharacters(1);
| + | |
| - | | + | |
| - | if(StartOfStatement)
| + | |
| - | {
| + | |
| - | /* * at start of statement means don't tokenise rest of statement, other than string literals */
| + | |
| - | while(!EndOfFile && !ErrorNum && IncomingBuffer[0] != ':' && IncomingBuffer[0] != '\n')
| + | |
| - | {
| + | |
| - | switch(IncomingBuffer[0])
| + | |
| - | {
| + | |
| - | default:
| + | |
| - | WriteByte(IncomingBuffer[0]);
| + | |
| - | EatCharacters(1);
| + | |
| - | break;
| + | |
| - | case '"':
| + | |
| - | if(!CopyStringLiteral())
| + | |
| - | return false;
| + | |
| - | break;
| + | |
| - | }
| + | |
| - | }
| + | |
| - | }
| + | |
| - | break;
| + | |
| - | case '"':
| + | |
| - | if(!CopyStringLiteral())
| + | |
| - | return false;
| + | |
| - | break;
| + | |
| - | }
| + | |
| - | }
| + | |
| - | else
| + | |
| - | {
| + | |
| - | Uint8 Flags = KeyWordTable[Token - 0x80].Flags; //make copy of flags, as we're about to throwaway Token
| + | |
| - | | + | |
| - | WriteByte(Token); //write token
| + | |
| - | EatCharacters(KeyWordTable[Token - 0x80].StrLen);
| + | |
| - | | + | |
| - | /*
| + | |
| - |
| + | |
| - | Effect token flags
| + | |
| - |
| + | |
| - | */
| + | |
| - | if(Flags & 0x08)
| + | |
| - | {
| + | |
| - | /* FN or PROC, so duplicate next set of alphanumerics without thought */
| + | |
| - | while(!ErrorNum && !EndOfFile && AlphaNumeric(IncomingBuffer[0]))
| + | |
| - | {
| + | |
| - | WriteByte(IncomingBuffer[0]);
| + | |
| - | EatCharacters(1);
| + | |
| - | }
| + | |
| - | }
| + | |
| - | | + | |
| - | if(Flags & 0x10)
| + | |
| - | {
| + | |
| - | /* tokenise line numbers for a bit */
| + | |
| - | if(!DoLineNumberTokeniser())
| + | |
| - | return false;
| + | |
| - | }
| + | |
| - | | + | |
| - | if(Flags & 0x20)
| + | |
| - | {
| + | |
| - | /* REM or DATA, so copy rest of line without tokenisation */
| + | |
| - | while(!ErrorNum && !EndOfFile && IncomingBuffer[0] != '\n')
| + | |
| - | {
| + | |
| - | WriteByte(IncomingBuffer[0]);
| + | |
| - | EatCharacters(1);
| + | |
| - | }
| + | |
| - | }
| + | |
| - | | + | |
| - | if(
| + | |
| - | (Flags & 0x40) && StartOfStatement
| + | |
| - | )
| + | |
| - | {
| + | |
| - | /* pseudo-variable flag */
| + | |
| - | Memory[Addr-1] += 0x40; //adjust just-written token
| + | |
| - | }
| + | |
| - | | + | |
| - | /* check if we now go into middle of statement */
| + | |
| - | if(Flags & 0x02)
| + | |
| - | StartOfStatement = false;
| + | |
| - | | + | |
| - | /* check if we now go into start of statement */
| + | |
| - | if(Flags & 0x04)
| + | |
| - | StartOfStatement = true;
| + | |
| - | }
| + | |
| - | }
| + | |
| - | | + | |
| - | EatCharacters(1); //either eat a '\n' or have no effect at all
| + | |
| - | | + | |
| - | return true;
| + | |
| - | }
| + | |
| - | | + | |
| - | bool ImportBASIC(char *Filename, Uint8 *Mem)
| + | |
| - | {
| + | |
| - | /* store memory target to global var */
| + | |
| - | Memory = Mem;
| + | |
| - | ErrorNum = 0;
| + | |
| - | | + | |
| - | /* get the value of PAGE‚ insert BASIC code starting from there */
| + | |
| - | Addr = Memory[0x18] << 8;
| + | |
| - | | + | |
| - | /* validity check: does PAGE currently point to a 0x0d? */
| + | |
| - | if(Memory[Addr] != 0x0d)
| + | |
| - | {
| + | |
| - | ErrorNum = 1;
| + | |
| - | return false;
| + | |
| - | }
| + | |
| | | | |
| - | /* validity check: does TOP - 2 point to a 0x0d, 0xff? */
| + | <pre> |
| - | Uint16 TOPAddr = Memory[0x12] | (Memory[0x13] << 8);
| + | JOY_RIGHT = 1 |
| - | if(
| + | JOY_LEFT = 2 |
| - | (Memory[TOPAddr-2] != 0x0d) ||
| + | JOY_DOWN = 4 |
| - | (Memory[TOPAddr-1] != 0xff)
| + | JOY_UP = 8 |
| - | ) | + | JOY_FIRE = &10 |
| - | {
| + | |
| - | ErrorNum = 1;
| + | |
| - | return false;
| + | |
| - | }
| + | |
| | | | |
| - | /* open file, reset variables */
| + | JoyBits = &70 |
| - | inputfile = fopen(Filename, "rt");
| + | |
| - | IncomingPointer = 0;
| + | |
| - | CurLine = 1;
| + | |
| - | EndOfFile = false;
| + | |
| | | | |
| - | if(!inputfile) | |
| - | { | |
| - | ErrorNum = 2; | |
| - | return false; | |
| - | } | |
| | | | |
| - | /* fill input buffer */
| + | ._READ_JOYSTICK: |
| - | int c = 8; | + | LDA #&00 |
| - | while(c--) | + | STA JoyBits |
| - | GetCharacter();
| + | |
| | | | |
| - | while(!EndOfFile && !ErrorNum) | + | .left_right: |
| - | { | + | LDX #1 |
| - | /* get line number */
| + | LDA #&80 |
| - | /* skip white space and empty lines */
| + | JSR _OSBYTE |
| - | while(Token == ' ' || Token == '\t' || Token == '\r' || Token == '\n')
| + | LDA #0 |
| - | EatCharacters(1);
| + | CPY #&25 |
| - |
| + | BCS not_right |
| - | /* end of file? */
| + | ORA #JOY_RIGHT |
| - | if(EndOfFile) break;
| + | .not_right: |
| | + | CPY #&DB |
| | + | BCC not_left |
| | + | ORA #JOY_LEFT |
| | + | .not_left: |
| | + | STA JoyBits |
| | | | |
| - | /* now we should see a line number */
| + | .up_down: |
| - | if(!NumberStart || (NumberValue >= 32768))
| + | LDX #&02 |
| - | {
| + | LDA #&80 |
| - | ErrorNum = -1;
| + | JSR _OSBYTE |
| - | sprintf(DynamicErrorText, "Malformed line number at line %d", CurLine);
| + | LDA JoyBits |
| - | break;
| + | CPY #&25 |
| - | }
| + | BCS not_down |
| | + | ORA #JOY_DOWN |
| | + | .not_down: |
| | + | CPY #&DB |
| | + | BCC not_up |
| | + | ORA #JOY_UP |
| | + | .not_up: |
| | + | STA JoyBits |
| | | | |
| - | /* inject into memory */
| + | LDX #&00 |
| - | WriteByte(0x0d);
| + | LDA #&80 |
| - | WriteByte(NumberValue >> 8);
| + | JSR _OSBYTE |
| - | WriteByte(NumberValue&0xff);
| + | TXA |
| - | EatCharacters(NumberLength);
| + | LSR A |
| | + | BCC not_fire |
| | | | |
| - | /* read rest of line, record length */
| + | LDA JoyBits |
| - | Uint16 LengthAddr = Addr; WriteByte(0);
| + | ORA #JOY_FIRE |
| - | if(!EncodeLine())
| + | STA JoyBits |
| - | break;
| + | |
| - | Memory[LengthAddr] = (Uint8)(Addr - LengthAddr + 3);
| + | |
| - | } | + | |
| | | | |
| - | /* write "end of program" */
| + | .not_fire: |
| - | WriteByte(0x0d);
| + | RTS |
| - | WriteByte(0xff);
| + | |
| - |
| + | |
| - | /* write TOP */
| + | |
| - | Memory[0x12] = Addr&0xff;
| + | |
| - | Memory[0x13] = Addr >> 8; | + | |
| | | | |
| - | fclose(inputfile);
| + | </pre> |
| - | return ErrorNum ? false : true;
| + | |
| - | }</pre>
| + | |