diff --git a/src/BASIC.cpp b/src/BASIC.cpp
deleted file mode 100644
index f27e3ea..0000000
--- a/src/BASIC.cpp
+++ /dev/null
@@ -1,829 +0,0 @@
-/*************************************************************************************************/
-/**
- BASIC.cpp
-
- Contains routines for tokenising/detokenising BBC BASIC programs.
-
- Modified from code by Thomas Harte.
-
- Copyright (C) Thomas Harte
-
- This file is part of BeebAsm.
-
- BeebAsm is free software: you can redistribute it and/or modify it under the terms of the GNU
- General Public License as published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- BeebAsm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
- even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with BeebAsm, as
- COPYING.txt. If not, see .
-*/
-/*************************************************************************************************/
-
-#include "BASIC.h"
-#include
-#include
-#include
-#include
-
-/*
-
- 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
-{
- KeyWord(const char* name, Uint8 flags)
- : Name(name), Flags(flags), StrLen(0), Next(NULL)
- {}
-
- const char *Name;
- Uint8 Flags;
- unsigned int StrLen;
- struct KeyWord *Next;
-};
-
-struct KeyWord KeyWordTable[0x80] =
-{
- /* 0x80 */
- KeyWord("AND", 0x00), KeyWord("DIV", 0x00), KeyWord("EOR", 0x00), KeyWord("MOD", 0x00),
- KeyWord("OR", 0x00), KeyWord("ERROR",0x04), KeyWord("LINE", 0x00), KeyWord("OFF", 0x00),
-
- /* 0x88 */
- KeyWord("STEP", 0x00), KeyWord("SPC", 0x00), KeyWord("TAB(", 0x00), KeyWord("ELSE", 0x14),
- KeyWord("THEN", 0x14), KeyWord("", 0x00), KeyWord("OPENIN",0x00), KeyWord("PTR", 0x43),
-
- /* 0x90 */
- KeyWord("PAGE", 0x43), KeyWord("TIME", 0x43), KeyWord("LOMEM",0x43), KeyWord("HIMEM",0x43),
- KeyWord("ABS", 0x00), KeyWord("ACS", 0x00), KeyWord("ADVAL",0x00), KeyWord("ASC", 0x00),
-
- /* 0x98 */
- KeyWord("ASN", 0x00), KeyWord("ATN", 0x00), KeyWord("BGET", 0x01), KeyWord("COS", 0x00),
- KeyWord("COUNT",0x01), KeyWord("DEG", 0x00), KeyWord("ERL", 0x01), KeyWord("ERR", 0x01),
-
- /* 0xa0 */
- KeyWord("EVAL", 0x00), KeyWord("EXP", 0x00), KeyWord("EXT", 0x01), KeyWord("FALSE",0x01),
- KeyWord("FN", 0x08), KeyWord("GET", 0x00), KeyWord("INKEY",0x00), KeyWord("INSTR(",0x00),
-
- /* 0xa8 */
- KeyWord("INT", 0x00), KeyWord("LEN", 0x00), KeyWord("LN", 0x00), KeyWord("LOG", 0x00),
- KeyWord("NOT", 0x00), KeyWord("OPENUP",0x00), KeyWord("OPENOUT",0x00), KeyWord("PI", 0x01),
-
- /* 0xb0 */
- KeyWord("POINT(",0x00), KeyWord("POS", 0x01), KeyWord("RAD", 0x00), KeyWord("RND", 0x01),
- KeyWord("SGN", 0x00), KeyWord("SIN", 0x00), KeyWord("SQR", 0x00), KeyWord("TAN", 0x00),
-
- /* 0xb8 */
- KeyWord("TO", 0x00), KeyWord("TRUE", 0x01), KeyWord("USR", 0x00), KeyWord("VAL", 0x00),
- KeyWord("VPOS", 0x01), KeyWord("CHR$", 0x00), KeyWord("GET$", 0x00), KeyWord("INKEY$",0x00),
-
- /* 0xc0 */
- KeyWord("LEFT$(",0x00), KeyWord("MID$(",0x00), KeyWord("RIGHT$(",0x00), KeyWord("STR$", 0x00),
- KeyWord("STRING$(",0x00), KeyWord("EOF", 0x01), KeyWord("AUTO", 0x10), KeyWord("DELETE",0x10),
-
- /* 0xc8 */
- KeyWord("LOAD", 0x02), KeyWord("LIST", 0x10), KeyWord("NEW", 0x01), KeyWord("OLD", 0x01),
- KeyWord("RENUMBER",0x10), KeyWord("SAVE", 0x02), KeyWord("", 0x00), KeyWord("PTR", 0x00),
-
- /* 0xd0 */
- KeyWord("PAGE", 0x00), KeyWord("TIME", 0x01), KeyWord("LOMEM",0x00), KeyWord("HIMEM",0x00),
- KeyWord("SOUND",0x02), KeyWord("BPUT", 0x03), KeyWord("CALL", 0x02), KeyWord("CHAIN",0x02),
-
- /* 0xd8 */
- KeyWord("CLEAR",0x01), KeyWord("CLOSE",0x03), KeyWord("CLG", 0x01), KeyWord("CLS", 0x01),
- KeyWord("DATA", 0x20), KeyWord("DEF", 0x00), KeyWord("DIM", 0x02), KeyWord("DRAW", 0x02),
-
- /* 0xe0 */
- KeyWord("END", 0x01), KeyWord("ENDPROC",0x01), KeyWord("ENVELOPE",0x02), KeyWord("FOR", 0x02),
- KeyWord("GOSUB",0x12), KeyWord("GOTO", 0x12), KeyWord("GCOL", 0x02), KeyWord("IF", 0x02),
-
- /* 0xe8 */
- KeyWord("INPUT",0x02), KeyWord("LET", 0x04), KeyWord("LOCAL",0x02), KeyWord("MODE", 0x02),
- KeyWord("MOVE", 0x02), KeyWord("NEXT", 0x02), KeyWord("ON", 0x02), KeyWord("VDU", 0x02),
-
- /* 0xf0 */
- KeyWord("PLOT", 0x02), KeyWord("PRINT",0x02), KeyWord("PROC", 0x0a), KeyWord("READ", 0x02),
- KeyWord("REM", 0x20), KeyWord("REPEAT",0x00), KeyWord("REPORT",0x01), KeyWord("RESTORE",0x12),
-
- /* 0xf8 */
- KeyWord("RETURN",0x01), KeyWord("RUN", 0x01), KeyWord("STOP", 0x01), KeyWord("COLOUR",0x02),
- KeyWord("TRACE",0x12), KeyWord("UNTIL",0x02), KeyWord("WIDTH",0x02), KeyWord("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
-
-*/
-const 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"
-};
-std::ostringstream DynamicErrorText;
-std::string DynamicErrorTextString;
-
-int ErrorNum;
-
-const char *GetBASICError()
-{
- DynamicErrorTextString = DynamicErrorText.str();
- return ErrorNum >= 0 ? ErrorTable[ErrorNum] : DynamicErrorTextString.c_str();
-}
-
-int GetBASICErrorNum()
-{
- return ErrorNum;
-}
-
-
-/*
-
- 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 = static_cast(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(const 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;
- if(IncomingBuffer[0] == '\n')
- {
- --CurLine;
- }
- DynamicErrorText << "Malformed string literal on line " << 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;
- else if(Token == '=') // an equals sign always switches the tokeniser back to "middle of statement" mode
- StartOfStatement = false;
-
- // 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 */
- // Bugfix RTW - * commands should not be terminated by colons
- 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;
- }
- }
-
- if (!EndOfFile && Token == '\n' && !ErrorNum)
- EatCharacters(1); // Eat a '\n'
-
- return true;
-}
-
-bool ImportBASIC(const char *Filename, Uint8 *Mem, int* Size)
-{
- /* store memory target to global var */
- Memory = Mem;
- ErrorNum = 0;
- Addr = 0;
-
-#if 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? */
- Uint16 TOPAddr = Memory[0x12] | (Memory[0x13] << 8);
- if(
- (Memory[TOPAddr-2] != 0x0d) ||
- (Memory[TOPAddr-1] != 0xff)
- )
- {
- ErrorNum = 1;
- return false;
- }
-#endif
-
- /* open file, reset variables */
- inputfile = fopen(Filename, "rt");
- IncomingPointer = 0;
- CurLine = 1;
- EndOfFile = false;
-
- if(!inputfile)
- {
- ErrorNum = 2;
- return false;
- }
-
- /* fill input buffer */
- int c = 8;
- while(c--)
- GetCharacter();
-
- /* initialise this to 0 for use with automatic line numbering */
- unsigned int LastLineNumber = 0;
-
- while(!EndOfFile && !ErrorNum)
- {
- /* get line number */
- /* skip white space and empty lines */
- while(!EndOfFile && (Token == ' ' || Token == '\t' || Token == '\r' || Token == '\n'))
- EatCharacters(1);
-
- /* end of file? */
- if(EndOfFile) break;
-
- /* now we may see a line number */
- if(NumberStart)
- {
- if (NumberValue <= LastLineNumber)
- {
- ErrorNum = -1;
- DynamicErrorText << "Out of sequence line numbers (" << LastLineNumber << " followed by " << NumberValue << ") at line " << CurLine;
- break;
- }
- LastLineNumber = NumberValue;
- EatCharacters(NumberLength);
- }
- else
- {
- /* auto-number the line instead */
- LastLineNumber += 1;
- }
- if(LastLineNumber >= 32768)
- {
- ErrorNum = -1;
- DynamicErrorText << "Malformed line number at line " << CurLine;
- break;
- }
- /* inject into memory */
- WriteByte(0x0d);
- WriteByte(LastLineNumber>> 8);
- WriteByte(LastLineNumber&0xff);
-
- /* read rest of line, record length */
- Uint16 LengthAddr = Addr; WriteByte(0);
- if(!EncodeLine())
- break;
-
- Uint16 Length = Addr - LengthAddr + 3;
- if(Length >= 256)
- {
- ErrorNum = -1;
- /* CurLine - 1 because we've incremented it on seeing '\n' */
- DynamicErrorText << "Overly long line at line " << CurLine - 1;
- break;
- }
- Memory[LengthAddr] = static_cast(Length);
- }
-
- /* write "end of program" */
- WriteByte(0x0d);
- WriteByte(0xff);
-
-#if 0
- /* write TOP */
- Memory[0x12] = Addr&0xff;
- Memory[0x13] = Addr >> 8;
-#endif
-
- // Return size of tokenised code
- if (Size != NULL)
- {
- *Size = Addr;
- }
-
- fclose(inputfile);
- return ErrorNum ? false : true;
-}
diff --git a/src/BASIC.h b/src/BASIC.h
deleted file mode 100644
index 46fa30e..0000000
--- a/src/BASIC.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*************************************************************************************************/
-/**
- BASIC.h
-
- Contains routines for tokenising/detokenising BBC BASIC programs.
-
- Modified from code by Thomas Harte.
-
- Copyright (C) Thomas Harte
-
- This file is part of BeebAsm.
-
- BeebAsm is free software: you can redistribute it and/or modify it under the terms of the GNU
- General Public License as published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- BeebAsm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
- even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with BeebAsm, as
- COPYING.txt. If not, see .
-*/
-/*************************************************************************************************/
-
-#ifndef BASIC_H_
-#define BASIC_H_
-
-typedef unsigned char Uint8;
-typedef unsigned short Uint16;
-
-void SetupBASICTables();
-const char *GetBASICError();
-int GetBASICErrorNum();
-bool ExportBASIC(const char *Filename, Uint8 *Memory);
-bool ImportBASIC(const char *Filename, Uint8 *Mem, int* Size);
-
-
-#endif // BASIC_H_
diff --git a/src/VS2010/BeebAsm.vcxproj b/src/VS2010/BeebAsm.vcxproj
index 60fab5e..e9cfe68 100644
--- a/src/VS2010/BeebAsm.vcxproj
+++ b/src/VS2010/BeebAsm.vcxproj
@@ -80,7 +80,7 @@
-
+
@@ -95,10 +95,11 @@
+
-
+
@@ -112,6 +113,7 @@
+
diff --git a/src/VS2010/BeebAsm.vcxproj.filters b/src/VS2010/BeebAsm.vcxproj.filters
index 47a7342..0b1ff7c 100644
--- a/src/VS2010/BeebAsm.vcxproj.filters
+++ b/src/VS2010/BeebAsm.vcxproj.filters
@@ -51,9 +51,6 @@
Source Files
-
- Source Files
-
Source Files
@@ -66,6 +63,12 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
@@ -95,9 +98,6 @@
Header Files
-
- Header Files
-
Header Files
@@ -116,5 +116,11 @@
Header Files
+
+ Header Files
+
+
+ Header Files
+
\ No newline at end of file
diff --git a/src/basic_tokens.cpp b/src/basic_tokens.cpp
new file mode 100644
index 0000000..70aceed
--- /dev/null
+++ b/src/basic_tokens.cpp
@@ -0,0 +1,141 @@
+/*************************************************************************************************
+
+ basic_tokens.cpp - a table of BBC BASIC keywords
+
+*************************************************************************************************/
+
+#include "basic_tokens.h"
+
+// Return name and length of name
+#define NL(N) N, sizeof(N)-1
+
+const token token_list[] = {
+ { NL("AND"), 0x80, 0 },
+ { NL("ABS"), 0x94, 0 },
+ { NL("ACS"), 0x95, 0 },
+ { NL("ADVAL"), 0x96, 0 },
+ { NL("ASC"), 0x97, 0 },
+ { NL("ASN"), 0x98, 0 },
+ { NL("ATN"), 0x99, 0 },
+ { NL("AUTO"), 0xC6, token_L_flag },
+ { NL("BGET"), 0x9A, token_C_flag },
+ { NL("BPUT"), 0xD5, token_M_flag | token_C_flag },
+ { NL("COLOUR"), 0xFB, token_M_flag },
+ { NL("CALL"), 0xD6, token_M_flag },
+ { NL("CHAIN"), 0xD7, token_M_flag },
+ { NL("CHR$"), 0xBD, 0 },
+ { NL("CLEAR"), 0xD8, token_C_flag },
+ { NL("CLOSE"), 0xD9, token_M_flag | token_C_flag },
+ { NL("CLG"), 0xDA, token_C_flag },
+ { NL("CLS"), 0xDB, token_C_flag },
+ { NL("COS"), 0x9B, 0 },
+ { NL("COUNT"), 0x9C, token_C_flag },
+ { NL("DATA"), 0xDC, token_R_flag },
+ { NL("DEG"), 0x9D, 0 },
+ { NL("DEF"), 0xDD, 0 },
+ { NL("DELETE"), 0xC7, token_L_flag },
+ { NL("DIV"), 0x81, 0 },
+ { NL("DIM"), 0xDE, token_M_flag },
+ { NL("DRAW"), 0xDF, token_M_flag },
+ { NL("ENDPROC"), 0xE1, token_C_flag },
+ { NL("END"), 0xE0, token_C_flag },
+ { NL("ENVELOPE"), 0xE2, token_M_flag },
+ { NL("ELSE"), 0x8B, token_L_flag | token_S_flag },
+ { NL("EVAL"), 0xA0, 0 },
+ { NL("ERL"), 0x9E, token_C_flag },
+ { NL("ERROR"), 0x85, token_S_flag },
+ { NL("EOF"), 0xC5, token_C_flag },
+ { NL("EOR"), 0x82, 0 },
+ { NL("ERR"), 0x9F, token_C_flag },
+ { NL("EXP"), 0xA1, 0 },
+ { NL("EXT"), 0xA2, token_C_flag },
+ { NL("FOR"), 0xE3, token_M_flag },
+ { NL("FALSE"), 0xA3, token_C_flag },
+ { NL("FN"), 0xA4, token_F_flag },
+ { NL("GOTO"), 0xE5, token_L_flag | token_M_flag },
+ { NL("GET$"), 0xBE, 0 },
+ { NL("GET"), 0xA5, 0 },
+ { NL("GOSUB"), 0xE4, token_L_flag | token_M_flag },
+ { NL("GCOL"), 0xE6, token_M_flag },
+ { NL("HIMEM"), 0x93, token_P_flag | token_M_flag | token_C_flag },
+ { NL("INPUT"), 0xE8, token_M_flag },
+ { NL("IF"), 0xE7, token_M_flag },
+ { NL("INKEY$"), 0xBF, 0 },
+ { NL("INKEY"), 0xA6, 0 },
+ { NL("INT"), 0xA8, 0 },
+ { NL("INSTR("), 0xA7, 0 },
+ { NL("LIST"), 0xC9, token_L_flag },
+ { NL("LINE"), 0x86, 0 },
+ { NL("LOAD"), 0xC8, token_M_flag },
+ { NL("LOMEM"), 0x92, token_P_flag | token_M_flag | token_C_flag },
+ { NL("LOCAL"), 0xEA, token_M_flag },
+ { NL("LEFT$("), 0xC0, 0 },
+ { NL("LEN"), 0xA9, 0 },
+ { NL("LET"), 0xE9, token_S_flag },
+ { NL("LOG"), 0xAB, 0 },
+ { NL("LN"), 0xAA, 0 },
+ { NL("MID$("), 0xC1, 0 },
+ { NL("MODE"), 0xEB, token_M_flag },
+ { NL("MOD"), 0x83, 0 },
+ { NL("MOVE"), 0xEC, token_M_flag },
+ { NL("NEXT"), 0xED, token_M_flag },
+ { NL("NEW"), 0xCA, token_C_flag },
+ { NL("NOT"), 0xAC, 0 },
+ { NL("OLD"), 0xCB, token_C_flag },
+ { NL("ON"), 0xEE, token_M_flag },
+ { NL("OFF"), 0x87, 0 },
+ { NL("OR"), 0x84, 0 },
+ { NL("OPENIN"), 0x8E, 0 },
+ { NL("OPENOUT"), 0xAE, 0 },
+ { NL("OPENUP"), 0xAD, 0 },
+ { NL("OSCLI"), 0xFF, token_M_flag },
+ { NL("PRINT"), 0xF1, token_M_flag },
+ { NL("PAGE"), 0x90, token_P_flag | token_M_flag | token_C_flag },
+ { NL("PTR"), 0x8F, token_P_flag | token_M_flag | token_C_flag },
+ { NL("PI"), 0xAF, token_C_flag },
+ { NL("PLOT"), 0xF0, token_M_flag },
+ { NL("POINT("), 0xB0, 0 },
+ { NL("PROC"), 0xF2, token_F_flag | token_M_flag },
+ { NL("POS"), 0xB1, token_C_flag },
+ { NL("RETURN"), 0xF8, token_C_flag },
+ { NL("REPEAT"), 0xF5, 0 },
+ { NL("REPORT"), 0xF6, token_C_flag },
+ { NL("READ"), 0xF3, token_M_flag },
+ { NL("REM"), 0xF4, token_R_flag },
+ { NL("RUN"), 0xF9, token_C_flag },
+ { NL("RAD"), 0xB2, 0 },
+ { NL("RESTORE"), 0xF7, token_L_flag | token_M_flag },
+ { NL("RIGHT$("), 0xC2, 0 },
+ { NL("RND"), 0xB3, token_C_flag },
+ { NL("RENUMBER"), 0xCC, token_L_flag },
+ { NL("STEP"), 0x88, 0 },
+ { NL("SAVE"), 0xCD, token_M_flag },
+ { NL("SGN"), 0xB4, 0 },
+ { NL("SIN"), 0xB5, 0 },
+ { NL("SQR"), 0xB6, 0 },
+ { NL("SPC"), 0x89, 0 },
+ { NL("STR$"), 0xC3, 0 },
+ { NL("STRING$("), 0xC4, 0 },
+ { NL("SOUND"), 0xD4, token_M_flag },
+ { NL("STOP"), 0xFA, token_C_flag },
+ { NL("TAN"), 0xB7, 0 },
+ { NL("THEN"), 0x8C, token_L_flag | token_S_flag },
+ { NL("TO"), 0xB8, 0 },
+ { NL("TAB("), 0x8A, 0 },
+ { NL("TRACE"), 0xFC, token_L_flag | token_M_flag },
+ { NL("TIME"), 0x91, token_P_flag | token_M_flag | token_C_flag },
+ { NL("TRUE"), 0xB9, token_C_flag },
+ { NL("UNTIL"), 0xFD, token_M_flag },
+ { NL("USR"), 0xBA, 0 },
+ { NL("VDU"), 0xEF, token_M_flag },
+ { NL("VAL"), 0xBB, 0 },
+ { NL("VPOS"), 0xBC, token_C_flag },
+ { NL("WIDTH"), 0xFE, token_M_flag },
+ { NL("PAGE"), 0xD0, 0 },
+ { NL("PTR"), 0xCF, 0 },
+ { NL("TIME"), 0xD1, 0 },
+ { NL("LOMEM"), 0xD2, 0 },
+ { NL("HIMEM"), 0xD3, 0 },
+};
+
+int token_list_length = ARRAY_LENGTH(token_list);
diff --git a/src/basic_tokens.h b/src/basic_tokens.h
new file mode 100644
index 0000000..0b13244
--- /dev/null
+++ b/src/basic_tokens.h
@@ -0,0 +1,36 @@
+/*************************************************************************************************
+
+ basic_tokens.h - a table of BBC BASIC keywords
+
+*************************************************************************************************/
+
+#ifndef _TABLES_H_
+#define _TABLES_H_
+
+#define ARRAY_LENGTH(a) (sizeof(a)/sizeof(a[0]))
+
+typedef unsigned char byte;
+
+enum token_flags
+{
+ token_C_flag = 0x01,
+ token_M_flag = 0x02,
+ token_S_flag = 0x04,
+ token_F_flag = 0x08,
+ token_L_flag = 0x10,
+ token_R_flag = 0x20,
+ token_P_flag = 0x40
+};
+
+struct token
+{
+ const char* name;
+ byte length;
+ byte code;
+ byte flags;
+};
+
+extern const token token_list[];
+extern int token_list_length;
+
+#endif // _TABLES_H
diff --git a/src/commands.cpp b/src/commands.cpp
index 20e1d79..d0e9a14 100644
--- a/src/commands.cpp
+++ b/src/commands.cpp
@@ -37,7 +37,7 @@
#include "sourcefile.h"
#include "asmexception.h"
#include "discimage.h"
-#include "BASIC.h"
+#include "tokenize.h"
#include "random.h"
@@ -1606,34 +1606,30 @@ void LineParser::HandlePutBasic()
if ( GlobalData::Instance().IsSecondPass() &&
GlobalData::Instance().UsesDiscImage() )
{
- Uint8* buffer = new Uint8[ 0x10000 ];
- int fileSize;
- bool bSuccess = ImportBASIC( hostFilename.c_str(), buffer, &fileSize );
-
- if (!bSuccess)
+ FILE* basic_file = fopen(hostFilename.c_str(), "rb");
+ if (!basic_file)
{
- if (GetBASICErrorNum() == 2)
- {
- AsmException_AssembleError_FileOpen e;
- e.SetString( m_line );
- e.SetColumn( m_column );
- throw e;
- }
- else
- {
- std::string message = hostFilename + ": " + GetBASICError();
- throw AsmException_UserError( m_line, m_column, message );
- }
+ AsmException_AssembleError_FileOpen e;
+ e.SetString( m_line );
+ e.SetColumn( m_column );
+ throw e;
+ }
+ std::vector tokenized;
+ TokenizeError err = tokenize_file(basic_file, tokenized);
+ fclose(basic_file);
+ if (err.IsError())
+ {
+ std::stringstream message;
+ message << hostFilename << ":" << err.lineNumber << ": " << err.messageText;
+ throw AsmException_UserError( m_line, m_column, message.str() );
}
// disc image version of the save
GlobalData::Instance().GetDiscImage()->AddFile( beebFilename.c_str(),
- reinterpret_cast< unsigned char* >( buffer ),
+ tokenized.data(),
0xFFFF1900,
0xFFFF8023,
- fileSize );
-
- delete [] buffer;
+ tokenized.size() );
}
}
diff --git a/src/main.cpp b/src/main.cpp
index 05bd85f..4db4ddd 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -34,7 +34,6 @@
#include "objectcode.h"
#include "symboltable.h"
#include "discimage.h"
-#include "BASIC.h"
#include "macro.h"
#include "random.h"
@@ -313,7 +312,6 @@ int main( int argc, char* argv[] )
ObjectCode::Create();
MacroTable::Create();
- SetupBASICTables();
time_t randomSeed = time( NULL );
diff --git a/src/tokenize.cpp b/src/tokenize.cpp
new file mode 100644
index 0000000..545d8c9
--- /dev/null
+++ b/src/tokenize.cpp
@@ -0,0 +1,506 @@
+/*************************************************************************************************
+
+ tokenize.cpp - tokenize BBC BASIC
+
+ Copyright (C) Charles Reilly 2022
+
+ This file is licensed under the GNU General Public License version 3
+
+*************************************************************************************************/
+
+#include
+#include
+#include
+#include "basic_tokens.h"
+#include "tokenize.h"
+
+class Reader
+{
+public:
+ Reader(FILE* file) : m_file(file)
+ {
+ m_line = 1;
+ m_current = 0;
+ m_end = false;
+ m_errno = 0;
+ m_lastcr = false;
+ Next();
+ }
+
+ int LineNumber() const { return m_line; }
+ char Current() const { return m_current; }
+ bool End() const { return m_end; }
+
+ void Next()
+ {
+ if (m_current == 0x0D)
+ {
+ if (m_end)
+ {
+ return;
+ }
+ m_line++;
+ }
+ int next = fgetc(m_file);
+ if (m_lastcr && (next == 0x0A))
+ {
+ next = fgetc(m_file);
+ }
+ if (next == EOF)
+ {
+ if (ferror(m_file))
+ {
+ m_errno = errno;
+ }
+ // m_lastcr no longer matters
+ m_end = true;
+ m_current = 0x0D;
+ }
+ else if (next == 0x0A)
+ {
+ // Convert LF to CR
+ m_lastcr = false;
+ m_current = 0x0D;
+ }
+ else
+ {
+ m_lastcr = (next == 0x0D);
+ m_current = next;
+ }
+ }
+
+private:
+ FILE* m_file;
+ bool m_end;
+ bool m_lastcr;
+ char m_current;
+ int m_errno;
+ int m_line;
+};
+
+class Writer
+{
+public:
+ void Init(int line_number)
+ {
+ m_fail = false;
+ m_buffer[0] = 0x0D;
+ m_buffer[1] = (line_number >> 8) & 0xFF;
+ m_buffer[2] = line_number & 0xFF;
+ m_length = 4;
+ }
+ bool Finish()
+ {
+ if (!m_fail)
+ {
+ assert(m_length < 0x100);
+ m_buffer[3] = m_length;
+ }
+ return !m_fail;
+ }
+ void write(char c)
+ {
+ if (m_length < static_cast(sizeof(m_buffer)))
+ {
+ m_buffer[m_length++] = c;
+ }
+ else
+ {
+ m_fail = true;
+ }
+ }
+ int Length() const { return m_length; }
+ const char* Data() const { return m_buffer; }
+
+private:
+ // Buffer cannot be any bigger because writes must fail when size exceeds byte
+ char m_buffer[255];
+ int m_length;
+ bool m_fail;
+};
+
+typedef bool (CHAR_TEST)(char c);
+
+void skip_write(CHAR_TEST f, Reader &reader, Writer &writer)
+{
+ while (f(reader.Current()))
+ {
+ writer.write(reader.Current());
+ reader.Next();
+ }
+}
+
+static bool is_not_cr(char c)
+{
+ return c != 0x0D;
+}
+
+static bool is_alpha(char c)
+{
+ return ('A' <= c) && (c <= 'Z');
+}
+
+static bool is_digit(char c)
+{
+ return ('0' <= c) && (c <= '9');
+}
+
+static bool is_alpha_digit(char c)
+{
+ // Note that this includes backtick aka pounds sterling
+ return (('_' <= c) && (c <= 'z')) || (('A' <= c) && (c <= 'Z')) || is_digit(c);
+}
+
+static bool is_dot_digit(char c)
+{
+ return c == '.' || is_digit(c);
+}
+
+static bool is_hex_digit(char c)
+{
+ return (('A' <= c) && (c <= 'F')) || is_digit(c);
+}
+
+static bool tokenize_linenum(Reader &reader, Writer &writer)
+{
+ char buffer[6];
+ int zero_count = 0;
+ while (reader.Current() == '0')
+ {
+ ++zero_count;
+ reader.Next();
+ }
+ int index = 0;
+ int acc = 0;
+ while (is_digit(reader.Current()))
+ {
+ char c = reader.Current();
+ acc = 10 * acc + (c - '0');
+ if (acc >= 0x8000)
+ {
+ while (zero_count--)
+ writer.write('0');
+ for (int i = 0; i != index; ++i)
+ {
+ writer.write(buffer[i]);
+ }
+ skip_write(is_digit, reader, writer);
+ return false;
+ }
+ buffer[index++] = c;
+ reader.Next();
+ }
+ writer.write(static_cast(0x8D));
+ writer.write((((acc & 0xC000) >> 12) | ((acc & 0xC0) >> 2)) ^ 0x54);
+ writer.write((acc & 0x3F) | 0x40);
+ writer.write((acc >> 8) | 0x40);
+ return true;
+}
+
+static char read_next_char(Reader &reader)
+{
+ reader.Next();
+ return reader.Current();
+}
+
+// Parse the characters from reader starting with the current character.
+// If they form a keyword (and they are not C flagged with a digit or letter following) then:
+// Consume all the characters.
+// Return the keyword.
+// If they form the prefix of a keyword then:
+// Consume all the matching prefix characters.
+// Write those same characters to the output.
+// If the last matching character was alphabetic (i.e. not '$') then:
+// Consume and write all the remaining is_alpha_digit characters.
+// return 0
+// If there were no prefix characters (e.g. just 'Q' or '_') then
+// Consume and write all the remaining is_alpha_digit characters.
+// return 0
+static const token* parse_keyword(Reader &reader, Writer &writer)
+{
+ // The number of characters consumed and matched from ReadStream
+ int match_count = 0;
+ // The name of the token whose first match_count characters are those read from ReadStream
+ const char* match_name;
+
+ for (int token_index = 0; token_index != token_list_length; ++token_index)
+ {
+ const token tok = token_list[token_index];
+ if (!match_count || ((match_count <= tok.length) && !memcmp(match_name, tok.name, match_count)))
+ {
+ // Prefix seen so far matches this token - consume more matching characters
+ while ((match_count < tok.length) && (reader.Current() == tok.name[match_count]))
+ {
+ reader.Next();
+ ++match_count;
+ }
+ if (match_count)
+ {
+ if (match_count == tok.length)
+ {
+ // Complete match
+ if (tok.flags & token_C_flag)
+ {
+ // Has C flag, so cannot be followed by letters or digits
+ if (is_alpha_digit(reader.Current()))
+ {
+ for (int i = 0; i != match_count; ++i)
+ {
+ writer.write(tok.name[i]);
+ }
+ skip_write(is_alpha_digit, reader, writer);
+ return 0;
+ }
+ }
+ return &token_list[token_index];
+ }
+ if (reader.Current() == '.')
+ {
+ // Abbreviation so consume dot and return
+ reader.Next();
+ return &token_list[token_index];
+ }
+ match_name = tok.name;
+ }
+ }
+ }
+ if (match_count)
+ {
+ // Found a prefix but it wasn't a match so output it
+ for (int i = 0; i != match_count; ++i)
+ {
+ writer.write(match_name[i]);
+ }
+ if (is_alpha(match_name[match_count-1]))
+ {
+ skip_write(is_alpha_digit, reader, writer);
+ }
+ }
+ else
+ {
+ skip_write(is_alpha_digit, reader, writer);
+ }
+ return 0;
+}
+
+void tokenize_line(Reader &reader, Writer &writer)
+{
+ // From the prompt BASIC starts in tokenize-numbers mode.
+ // This function works like the tokenizer used by AUTO.
+
+ bool start_of_line = true;
+ bool tokenize_numbers = false;
+
+ while (true)
+ {
+ char c = reader.Current();
+
+ if (c == 0x0D)
+ return;
+
+ if (c == ' ')
+ {
+ writer.write(c);
+ reader.Next();
+ continue;
+ }
+
+ if (c == '&')
+ {
+ writer.write(c);
+ reader.Next();
+ skip_write(is_hex_digit, reader, writer);
+ continue;
+ }
+
+ if (c == '\"')
+ {
+ writer.write(c);
+ do
+ {
+ c = read_next_char(reader);
+ if (c == 0x0D)
+ return;
+ writer.write(c);
+ }
+ while (c != '\"');
+ reader.Next();
+ continue;
+ }
+
+ if (c == ':')
+ {
+ writer.write(c);
+ reader.Next();
+ start_of_line = true;
+ tokenize_numbers = false;
+ continue;
+ }
+
+ if (c == ',')
+ {
+ writer.write(c);
+ reader.Next();
+ continue;
+ }
+
+ if (c == '*')
+ {
+ if (start_of_line)
+ {
+ skip_write(is_not_cr, reader, writer);
+ return;
+ }
+ writer.write(c);
+ reader.Next();
+ start_of_line = false;
+ tokenize_numbers = false;
+ continue;
+ }
+
+ if (is_dot_digit(c))
+ {
+ if ((c != '.') && tokenize_numbers)
+ {
+ tokenize_linenum(reader, writer);
+ continue;
+ }
+ skip_write(is_dot_digit, reader, writer);
+ start_of_line = false;
+ tokenize_numbers = false;
+ continue;
+ }
+
+ if (!is_alpha_digit(reader.Current()))
+ {
+ start_of_line = false;
+ tokenize_numbers = false;
+ writer.write(reader.Current());
+ reader.Next();
+ continue;
+ }
+
+ const token* keyword = parse_keyword(reader, writer);
+ if (!keyword)
+ {
+ start_of_line = false;
+ tokenize_numbers = false;
+ continue;
+ }
+ else
+ {
+ byte code = keyword->code;
+ byte flags = keyword->flags;
+
+ if ((flags & token_C_flag) && is_alpha_digit(reader.Current()))
+ {
+ // This should never happen because parse_keyword handles the C flag
+ assert(false);
+ start_of_line = false;
+ tokenize_numbers = false;
+ continue;
+ }
+
+ if ((flags & token_P_flag) && start_of_line)
+ {
+ code += 0x40;
+ }
+
+ writer.write(code);
+
+ if (flags & token_M_flag)
+ {
+ start_of_line = false;
+ tokenize_numbers = false;
+ }
+
+ if (flags & token_S_flag)
+ {
+ start_of_line = true;
+ tokenize_numbers = false;
+ }
+
+ if (flags & token_F_flag)
+ {
+ skip_write(is_alpha_digit, reader, writer);
+ }
+
+ if (flags & token_L_flag)
+ {
+ tokenize_numbers = true;
+ }
+
+ if (flags & token_R_flag)
+ {
+ skip_write(is_not_cr, reader, writer);
+ return;
+ }
+ }
+ }
+}
+
+// Read a plain text BBC BASIC program from `file` and write it tokenized to `tokenized`
+TokenizeError tokenize_file(FILE* file, std::vector& tokenized)
+{
+ Reader reader(file);
+
+ int last_line = -1;
+
+ Writer writer;
+
+ while (!reader.End())
+ {
+ while (reader.Current() == ' ')
+ {
+ reader.Next();
+ }
+
+ int line = 0;
+ bool saw_digit = false;
+ while (is_digit(reader.Current()))
+ {
+ saw_digit = true;
+ line = 10 * line + (reader.Current() - '0');
+ if (line > 0x7FFF)
+ {
+ break;
+ }
+ reader.Next();
+ }
+
+ if (saw_digit)
+ {
+ if (line <= last_line)
+ {
+ return TokenizeError(reader.LineNumber(), "Line numbers must increase");
+ }
+ }
+ else
+ {
+ // Start auto-numbering at 1 but permit explicit line 0
+ line = last_line < 0 ? 1 : last_line + 1;
+ }
+ last_line = line;
+
+ if (line > 0x7FFF)
+ {
+ return TokenizeError(reader.LineNumber(), "Line number too big");
+ }
+
+ writer.Init(line);
+ tokenize_line(reader, writer);
+ if (!writer.Finish())
+ {
+ return TokenizeError(reader.LineNumber(), "Line too long after tokenizing");
+ }
+
+ // Write the line to the buffer
+ tokenized.insert(tokenized.end(), writer.Data(), writer.Data() + writer.Length());
+
+ reader.Next();
+ }
+ tokenized.push_back(0x0D);
+ tokenized.push_back(0xFF);
+
+ return TokenizeError();
+}
diff --git a/src/tokenize.h b/src/tokenize.h
new file mode 100644
index 0000000..1cb77d2
--- /dev/null
+++ b/src/tokenize.h
@@ -0,0 +1,37 @@
+/*************************************************************************************************
+
+ tokenize.h - tokenize BBC BASIC
+
+ Copyright (C) Charles Reilly 2022
+
+ This file is licensed under the GNU General Public License version 3
+
+*************************************************************************************************/
+
+#ifndef _TOKENIZE_H
+#define _TOKENIZE_H
+
+#include
+#include
+
+struct TokenizeError
+{
+ TokenizeError()
+ {
+ messageText = 0;
+ lineNumber = 0;
+ }
+ TokenizeError(int line, const char* message)
+ {
+ lineNumber = line;
+ messageText = message;
+ }
+ bool IsError() const { return messageText != 0; };
+ const char* messageText;
+ int lineNumber;
+};
+
+// Read a plain text BBC BASIC program from `file` and write it tokenized to `tokenized`
+TokenizeError tokenize_file(FILE* file, std::vector& tokenized);
+
+#endif // _TOKENIZE_H
diff --git a/test/3-directives/invalidbasic1.bas b/test/3-directives/invalidbasic1.bas
deleted file mode 100644
index 62eb4c3..0000000
--- a/test/3-directives/invalidbasic1.bas
+++ /dev/null
@@ -1 +0,0 @@
-10PRINT "Hello
diff --git a/test/3-directives/invalidbasic1.fail.6502 b/test/3-directives/invalidbasic1.fail.6502
deleted file mode 100644
index 4764ff4..0000000
--- a/test/3-directives/invalidbasic1.fail.6502
+++ /dev/null
@@ -1,9 +0,0 @@
-\ beebasm -do test.ssd
-
-org &2000
-.start
- rts
-.end
-
-save "test", start, end
-putbasic "invalidbasic1.bas", "ib1"
diff --git a/test/3-directives/putbasic/abbreviations.6502 b/test/3-directives/putbasic/abbreviations.6502
new file mode 100644
index 0000000..86bde06
--- /dev/null
+++ b/test/3-directives/putbasic/abbreviations.6502
@@ -0,0 +1 @@
+PUTBASIC "abbreviations.bas", "$.ABBRV"
diff --git a/test/3-directives/putbasic/abbreviations.bas b/test/3-directives/putbasic/abbreviations.bas
new file mode 100644
index 0000000..729fe84
--- /dev/null
+++ b/test/3-directives/putbasic/abbreviations.bas
@@ -0,0 +1,2804 @@
+AND
+ANDX
+AND*
+A.
+AN.
+AND.
+AX
+ANX
+ANDX
+ABS
+ABSX
+ABS*
+A.
+AB.
+ABS.
+AX
+ABX
+ABSX
+ACS
+ACSX
+ACS*
+A.
+AC.
+ACS.
+AX
+ACX
+ACSX
+ADVAL
+ADVALX
+ADVAL*
+A.
+AD.
+ADV.
+ADVA.
+ADVAL.
+AX
+ADX
+ADVX
+ADVAX
+ADVALX
+ASC
+ASCX
+ASC*
+A.
+AS.
+ASC.
+AX
+ASX
+ASCX
+ASN
+ASNX
+ASN*
+A.
+AS.
+ASN.
+AX
+ASX
+ASNX
+ATN
+ATNX
+ATN*
+A.
+AT.
+ATN.
+AX
+ATX
+ATNX
+AUTO
+AUTOX
+AUTO*
+A.
+AU.
+AUT.
+AUTO.
+AX
+AUX
+AUTX
+AUTOX
+BGET
+BGETX
+BGET*
+B.
+BG.
+BGE.
+BGET.
+BX
+BGX
+BGEX
+BGETX
+BPUT
+BPUTX
+BPUT*
+B.
+BP.
+BPU.
+BPUT.
+BX
+BPX
+BPUX
+BPUTX
+COLOUR
+COLOURX
+COLOUR*
+C.
+CO.
+COL.
+COLO.
+COLOU.
+COLOUR.
+CX
+COX
+COLX
+COLOX
+COLOUX
+COLOURX
+CALL
+CALLX
+CALL*
+C.
+CA.
+CAL.
+CALL.
+CX
+CAX
+CALX
+CALLX
+CHAIN
+CHAINX
+CHAIN*
+C.
+CH.
+CHA.
+CHAI.
+CHAIN.
+CX
+CHX
+CHAX
+CHAIX
+CHAINX
+CHR$
+CHR$X
+CHR$*
+C.
+CH.
+CHR.
+CHR$.
+CX
+CHX
+CHRX
+CHR$X
+CLEAR
+CLEARX
+CLEAR*
+C.
+CL.
+CLE.
+CLEA.
+CLEAR.
+CX
+CLX
+CLEX
+CLEAX
+CLEARX
+CLOSE
+CLOSEX
+CLOSE*
+C.
+CL.
+CLO.
+CLOS.
+CLOSE.
+CX
+CLX
+CLOX
+CLOSX
+CLOSEX
+CLG
+CLGX
+CLG*
+C.
+CL.
+CLG.
+CX
+CLX
+CLGX
+CLS
+CLSX
+CLS*
+C.
+CL.
+CLS.
+CX
+CLX
+CLSX
+COS
+COSX
+COS*
+C.
+CO.
+COS.
+CX
+COX
+COSX
+COUNT
+COUNTX
+COUNT*
+C.
+CO.
+COU.
+COUN.
+COUNT.
+CX
+COX
+COUX
+COUNX
+COUNTX
+DATA
+DATAX
+DATA*
+D.
+DA.
+DAT.
+DATA.
+DX
+DAX
+DATX
+DATAX
+DEG
+DEGX
+DEG*
+D.
+DE.
+DEG.
+DX
+DEX
+DEGX
+DEF
+DEFX
+DEF*
+D.
+DE.
+DEF.
+DX
+DEX
+DEFX
+DELETE
+DELETEX
+DELETE*
+D.
+DE.
+DEL.
+DELE.
+DELET.
+DELETE.
+DX
+DEX
+DELX
+DELEX
+DELETX
+DELETEX
+DIV
+DIVX
+DIV*
+D.
+DI.
+DIV.
+DX
+DIX
+DIVX
+DIM
+DIMX
+DIM*
+D.
+DI.
+DIM.
+DX
+DIX
+DIMX
+DRAW
+DRAWX
+DRAW*
+D.
+DR.
+DRA.
+DRAW.
+DX
+DRX
+DRAX
+DRAWX
+ENDPROC
+ENDPROCX
+ENDPROC*
+E.
+EN.
+END.
+ENDP.
+ENDPR.
+ENDPRO.
+ENDPROC.
+EX
+ENX
+ENDX
+ENDPX
+ENDPRX
+ENDPROX
+ENDPROCX
+END
+ENDX
+END*
+E.
+EN.
+END.
+EX
+ENX
+ENDX
+ENVELOPE
+ENVELOPEX
+ENVELOPE*
+E.
+EN.
+ENV.
+ENVE.
+ENVEL.
+ENVELO.
+ENVELOP.
+ENVELOPE.
+EX
+ENX
+ENVX
+ENVEX
+ENVELX
+ENVELOX
+ENVELOPX
+ENVELOPEX
+ELSE
+ELSEX
+ELSE*
+E.
+EL.
+ELS.
+ELSE.
+EX
+ELX
+ELSX
+ELSEX
+EVAL
+EVALX
+EVAL*
+E.
+EV.
+EVA.
+EVAL.
+EX
+EVX
+EVAX
+EVALX
+ERL
+ERLX
+ERL*
+E.
+ER.
+ERL.
+EX
+ERX
+ERLX
+ERROR
+ERRORX
+ERROR*
+E.
+ER.
+ERR.
+ERRO.
+ERROR.
+EX
+ERX
+ERRX
+ERROX
+ERRORX
+EOF
+EOFX
+EOF*
+E.
+EO.
+EOF.
+EX
+EOX
+EOFX
+EOR
+EORX
+EOR*
+E.
+EO.
+EOR.
+EX
+EOX
+EORX
+ERR
+ERRX
+ERR*
+E.
+ER.
+ERR.
+EX
+ERX
+ERRX
+EXP
+EXPX
+EXP*
+E.
+EX.
+EXP.
+EX
+EXX
+EXPX
+EXT
+EXTX
+EXT*
+E.
+EX.
+EXT.
+EX
+EXX
+EXTX
+FOR
+FORX
+FOR*
+F.
+FO.
+FOR.
+FX
+FOX
+FORX
+FALSE
+FALSEX
+FALSE*
+F.
+FA.
+FAL.
+FALS.
+FALSE.
+FX
+FAX
+FALX
+FALSX
+FALSEX
+FN
+FNX
+FN*
+F.
+FN.
+FX
+FNX
+GOTO
+GOTOX
+GOTO*
+G.
+GO.
+GOT.
+GOTO.
+GX
+GOX
+GOTX
+GOTOX
+GET$
+GET$X
+GET$*
+G.
+GE.
+GET.
+GET$.
+GX
+GEX
+GETX
+GET$X
+GET
+GETX
+GET*
+G.
+GE.
+GET.
+GX
+GEX
+GETX
+GOSUB
+GOSUBX
+GOSUB*
+G.
+GO.
+GOS.
+GOSU.
+GOSUB.
+GX
+GOX
+GOSX
+GOSUX
+GOSUBX
+GCOL
+GCOLX
+GCOL*
+G.
+GC.
+GCO.
+GCOL.
+GX
+GCX
+GCOX
+GCOLX
+HIMEM
+HIMEMX
+HIMEM*
+H.
+HI.
+HIM.
+HIME.
+HIMEM.
+HX
+HIX
+HIMX
+HIMEX
+HIMEMX
+INPUT
+INPUTX
+INPUT*
+I.
+IN.
+INP.
+INPU.
+INPUT.
+IX
+INX
+INPX
+INPUX
+INPUTX
+IF
+IFX
+IF*
+I.
+IF.
+IX
+IFX
+INKEY$
+INKEY$X
+INKEY$*
+I.
+IN.
+INK.
+INKE.
+INKEY.
+INKEY$.
+IX
+INX
+INKX
+INKEX
+INKEYX
+INKEY$X
+INKEY
+INKEYX
+INKEY*
+I.
+IN.
+INK.
+INKE.
+INKEY.
+IX
+INX
+INKX
+INKEX
+INKEYX
+INT
+INTX
+INT*
+I.
+IN.
+INT.
+IX
+INX
+INTX
+INSTR(
+INSTR(X
+INSTR(*
+I.
+IN.
+INS.
+INST.
+INSTR.
+INSTR(.
+IX
+INX
+INSX
+INSTX
+INSTRX
+INSTR(X
+LIST
+LISTX
+LIST*
+L.
+LI.
+LIS.
+LIST.
+LX
+LIX
+LISX
+LISTX
+LINE
+LINEX
+LINE*
+L.
+LI.
+LIN.
+LINE.
+LX
+LIX
+LINX
+LINEX
+LOAD
+LOADX
+LOAD*
+L.
+LO.
+LOA.
+LOAD.
+LX
+LOX
+LOAX
+LOADX
+LOMEM
+LOMEMX
+LOMEM*
+L.
+LO.
+LOM.
+LOME.
+LOMEM.
+LX
+LOX
+LOMX
+LOMEX
+LOMEMX
+LOCAL
+LOCALX
+LOCAL*
+L.
+LO.
+LOC.
+LOCA.
+LOCAL.
+LX
+LOX
+LOCX
+LOCAX
+LOCALX
+LEFT$(
+LEFT$(X
+LEFT$(*
+L.
+LE.
+LEF.
+LEFT.
+LEFT$.
+LEFT$(.
+LX
+LEX
+LEFX
+LEFTX
+LEFT$X
+LEFT$(X
+LEN
+LENX
+LEN*
+L.
+LE.
+LEN.
+LX
+LEX
+LENX
+LET
+LETX
+LET*
+L.
+LE.
+LET.
+LX
+LEX
+LETX
+LOG
+LOGX
+LOG*
+L.
+LO.
+LOG.
+LX
+LOX
+LOGX
+LN
+LNX
+LN*
+L.
+LN.
+LX
+LNX
+MID$(
+MID$(X
+MID$(*
+M.
+MI.
+MID.
+MID$.
+MID$(.
+MX
+MIX
+MIDX
+MID$X
+MID$(X
+MODE
+MODEX
+MODE*
+M.
+MO.
+MOD.
+MODE.
+MX
+MOX
+MODX
+MODEX
+MOD
+MODX
+MOD*
+M.
+MO.
+MOD.
+MX
+MOX
+MODX
+MOVE
+MOVEX
+MOVE*
+M.
+MO.
+MOV.
+MOVE.
+MX
+MOX
+MOVX
+MOVEX
+NEXT
+NEXTX
+NEXT*
+N.
+NE.
+NEX.
+NEXT.
+NX
+NEX
+NEXX
+NEXTX
+NEW
+NEWX
+NEW*
+N.
+NE.
+NEW.
+NX
+NEX
+NEWX
+NOT
+NOTX
+NOT*
+N.
+NO.
+NOT.
+NX
+NOX
+NOTX
+OLD
+OLDX
+OLD*
+O.
+OL.
+OLD.
+OX
+OLX
+OLDX
+ON
+ONX
+ON*
+O.
+ON.
+OX
+ONX
+OFF
+OFFX
+OFF*
+O.
+OF.
+OFF.
+OX
+OFX
+OFFX
+OR
+ORX
+OR*
+O.
+OR.
+OX
+ORX
+OPENIN
+OPENINX
+OPENIN*
+O.
+OP.
+OPE.
+OPEN.
+OPENI.
+OPENIN.
+OX
+OPX
+OPEX
+OPENX
+OPENIX
+OPENINX
+OPENOUT
+OPENOUTX
+OPENOUT*
+O.
+OP.
+OPE.
+OPEN.
+OPENO.
+OPENOU.
+OPENOUT.
+OX
+OPX
+OPEX
+OPENX
+OPENOX
+OPENOUX
+OPENOUTX
+OPENUP
+OPENUPX
+OPENUP*
+O.
+OP.
+OPE.
+OPEN.
+OPENU.
+OPENUP.
+OX
+OPX
+OPEX
+OPENX
+OPENUX
+OPENUPX
+OSCLI
+OSCLIX
+OSCLI*
+O.
+OS.
+OSC.
+OSCL.
+OSCLI.
+OX
+OSX
+OSCX
+OSCLX
+OSCLIX
+PRINT
+PRINTX
+PRINT*
+P.
+PR.
+PRI.
+PRIN.
+PRINT.
+PX
+PRX
+PRIX
+PRINX
+PRINTX
+PAGE
+PAGEX
+PAGE*
+P.
+PA.
+PAG.
+PAGE.
+PX
+PAX
+PAGX
+PAGEX
+PTR
+PTRX
+PTR*
+P.
+PT.
+PTR.
+PX
+PTX
+PTRX
+PI
+PIX
+PI*
+P.
+PI.
+PX
+PIX
+PLOT
+PLOTX
+PLOT*
+P.
+PL.
+PLO.
+PLOT.
+PX
+PLX
+PLOX
+PLOTX
+POINT(
+POINT(X
+POINT(*
+P.
+PO.
+POI.
+POIN.
+POINT.
+POINT(.
+PX
+POX
+POIX
+POINX
+POINTX
+POINT(X
+PROC
+PROCX
+PROC*
+P.
+PR.
+PRO.
+PROC.
+PX
+PRX
+PROX
+PROCX
+POS
+POSX
+POS*
+P.
+PO.
+POS.
+PX
+POX
+POSX
+RETURN
+RETURNX
+RETURN*
+R.
+RE.
+RET.
+RETU.
+RETUR.
+RETURN.
+RX
+REX
+RETX
+RETUX
+RETURX
+RETURNX
+REPEAT
+REPEATX
+REPEAT*
+R.
+RE.
+REP.
+REPE.
+REPEA.
+REPEAT.
+RX
+REX
+REPX
+REPEX
+REPEAX
+REPEATX
+REPORT
+REPORTX
+REPORT*
+R.
+RE.
+REP.
+REPO.
+REPOR.
+REPORT.
+RX
+REX
+REPX
+REPOX
+REPORX
+REPORTX
+READ
+READX
+READ*
+R.
+RE.
+REA.
+READ.
+RX
+REX
+REAX
+READX
+REM
+REMX
+REM*
+R.
+RE.
+REM.
+RX
+REX
+REMX
+RUN
+RUNX
+RUN*
+R.
+RU.
+RUN.
+RX
+RUX
+RUNX
+RAD
+RADX
+RAD*
+R.
+RA.
+RAD.
+RX
+RAX
+RADX
+RESTORE
+RESTOREX
+RESTORE*
+R.
+RE.
+RES.
+REST.
+RESTO.
+RESTOR.
+RESTORE.
+RX
+REX
+RESX
+RESTX
+RESTOX
+RESTORX
+RESTOREX
+RIGHT$(
+RIGHT$(X
+RIGHT$(*
+R.
+RI.
+RIG.
+RIGH.
+RIGHT.
+RIGHT$.
+RIGHT$(.
+RX
+RIX
+RIGX
+RIGHX
+RIGHTX
+RIGHT$X
+RIGHT$(X
+RND
+RNDX
+RND*
+R.
+RN.
+RND.
+RX
+RNX
+RNDX
+RENUMBER
+RENUMBERX
+RENUMBER*
+R.
+RE.
+REN.
+RENU.
+RENUM.
+RENUMB.
+RENUMBE.
+RENUMBER.
+RX
+REX
+RENX
+RENUX
+RENUMX
+RENUMBX
+RENUMBEX
+RENUMBERX
+STEP
+STEPX
+STEP*
+S.
+ST.
+STE.
+STEP.
+SX
+STX
+STEX
+STEPX
+SAVE
+SAVEX
+SAVE*
+S.
+SA.
+SAV.
+SAVE.
+SX
+SAX
+SAVX
+SAVEX
+SGN
+SGNX
+SGN*
+S.
+SG.
+SGN.
+SX
+SGX
+SGNX
+SIN
+SINX
+SIN*
+S.
+SI.
+SIN.
+SX
+SIX
+SINX
+SQR
+SQRX
+SQR*
+S.
+SQ.
+SQR.
+SX
+SQX
+SQRX
+SPC
+SPCX
+SPC*
+S.
+SP.
+SPC.
+SX
+SPX
+SPCX
+STR$
+STR$X
+STR$*
+S.
+ST.
+STR.
+STR$.
+SX
+STX
+STRX
+STR$X
+STRING$(
+STRING$(X
+STRING$(*
+S.
+ST.
+STR.
+STRI.
+STRIN.
+STRING.
+STRING$.
+STRING$(.
+SX
+STX
+STRX
+STRIX
+STRINX
+STRINGX
+STRING$X
+STRING$(X
+SOUND
+SOUNDX
+SOUND*
+S.
+SO.
+SOU.
+SOUN.
+SOUND.
+SX
+SOX
+SOUX
+SOUNX
+SOUNDX
+STOP
+STOPX
+STOP*
+S.
+ST.
+STO.
+STOP.
+SX
+STX
+STOX
+STOPX
+TAN
+TANX
+TAN*
+T.
+TA.
+TAN.
+TX
+TAX
+TANX
+THEN
+THENX
+THEN*
+T.
+TH.
+THE.
+THEN.
+TX
+THX
+THEX
+THENX
+TO
+TOX
+TO*
+T.
+TO.
+TX
+TOX
+TAB(
+TAB(X
+TAB(*
+T.
+TA.
+TAB.
+TAB(.
+TX
+TAX
+TABX
+TAB(X
+TRACE
+TRACEX
+TRACE*
+T.
+TR.
+TRA.
+TRAC.
+TRACE.
+TX
+TRX
+TRAX
+TRACX
+TRACEX
+TIME
+TIMEX
+TIME*
+T.
+TI.
+TIM.
+TIME.
+TX
+TIX
+TIMX
+TIMEX
+TRUE
+TRUEX
+TRUE*
+T.
+TR.
+TRU.
+TRUE.
+TX
+TRX
+TRUX
+TRUEX
+UNTIL
+UNTILX
+UNTIL*
+U.
+UN.
+UNT.
+UNTI.
+UNTIL.
+UX
+UNX
+UNTX
+UNTIX
+UNTILX
+USR
+USRX
+USR*
+U.
+US.
+USR.
+UX
+USX
+USRX
+VDU
+VDUX
+VDU*
+V.
+VD.
+VDU.
+VX
+VDX
+VDUX
+VAL
+VALX
+VAL*
+V.
+VA.
+VAL.
+VX
+VAX
+VALX
+VPOS
+VPOSX
+VPOS*
+V.
+VP.
+VPO.
+VPOS.
+VX
+VPX
+VPOX
+VPOSX
+WIDTH
+WIDTHX
+WIDTH*
+W.
+WI.
+WID.
+WIDT.
+WIDTH.
+WX
+WIX
+WIDX
+WIDTX
+WIDTHX
+PAGE
+PAGEX
+PAGE*
+P.
+PA.
+PAG.
+PAGE.
+PX
+PAX
+PAGX
+PAGEX
+PTR
+PTRX
+PTR*
+P.
+PT.
+PTR.
+PX
+PTX
+PTRX
+TIME
+TIMEX
+TIME*
+T.
+TI.
+TIM.
+TIME.
+TX
+TIX
+TIMX
+TIMEX
+LOMEM
+LOMEMX
+LOMEM*
+L.
+LO.
+LOM.
+LOME.
+LOMEM.
+LX
+LOX
+LOMX
+LOMEX
+LOMEMX
+HIMEM
+HIMEMX
+HIMEM*
+H.
+HI.
+HIM.
+HIME.
+HIMEM.
+HX
+HIX
+HIMX
+HIMEX
+HIMEMX
+A=AND
+A=ANDX
+A=AND*
+A=A.
+A=AN.
+A=AND.
+A=AX
+A=ANX
+A=ANDX
+A=ABS
+A=ABSX
+A=ABS*
+A=A.
+A=AB.
+A=ABS.
+A=AX
+A=ABX
+A=ABSX
+A=ACS
+A=ACSX
+A=ACS*
+A=A.
+A=AC.
+A=ACS.
+A=AX
+A=ACX
+A=ACSX
+A=ADVAL
+A=ADVALX
+A=ADVAL*
+A=A.
+A=AD.
+A=ADV.
+A=ADVA.
+A=ADVAL.
+A=AX
+A=ADX
+A=ADVX
+A=ADVAX
+A=ADVALX
+A=ASC
+A=ASCX
+A=ASC*
+A=A.
+A=AS.
+A=ASC.
+A=AX
+A=ASX
+A=ASCX
+A=ASN
+A=ASNX
+A=ASN*
+A=A.
+A=AS.
+A=ASN.
+A=AX
+A=ASX
+A=ASNX
+A=ATN
+A=ATNX
+A=ATN*
+A=A.
+A=AT.
+A=ATN.
+A=AX
+A=ATX
+A=ATNX
+A=AUTO
+A=AUTOX
+A=AUTO*
+A=A.
+A=AU.
+A=AUT.
+A=AUTO.
+A=AX
+A=AUX
+A=AUTX
+A=AUTOX
+A=BGET
+A=BGETX
+A=BGET*
+A=B.
+A=BG.
+A=BGE.
+A=BGET.
+A=BX
+A=BGX
+A=BGEX
+A=BGETX
+A=BPUT
+A=BPUTX
+A=BPUT*
+A=B.
+A=BP.
+A=BPU.
+A=BPUT.
+A=BX
+A=BPX
+A=BPUX
+A=BPUTX
+A=COLOUR
+A=COLOURX
+A=COLOUR*
+A=C.
+A=CO.
+A=COL.
+A=COLO.
+A=COLOU.
+A=COLOUR.
+A=CX
+A=COX
+A=COLX
+A=COLOX
+A=COLOUX
+A=COLOURX
+A=CALL
+A=CALLX
+A=CALL*
+A=C.
+A=CA.
+A=CAL.
+A=CALL.
+A=CX
+A=CAX
+A=CALX
+A=CALLX
+A=CHAIN
+A=CHAINX
+A=CHAIN*
+A=C.
+A=CH.
+A=CHA.
+A=CHAI.
+A=CHAIN.
+A=CX
+A=CHX
+A=CHAX
+A=CHAIX
+A=CHAINX
+A=CHR$
+A=CHR$X
+A=CHR$*
+A=C.
+A=CH.
+A=CHR.
+A=CHR$.
+A=CX
+A=CHX
+A=CHRX
+A=CHR$X
+A=CLEAR
+A=CLEARX
+A=CLEAR*
+A=C.
+A=CL.
+A=CLE.
+A=CLEA.
+A=CLEAR.
+A=CX
+A=CLX
+A=CLEX
+A=CLEAX
+A=CLEARX
+A=CLOSE
+A=CLOSEX
+A=CLOSE*
+A=C.
+A=CL.
+A=CLO.
+A=CLOS.
+A=CLOSE.
+A=CX
+A=CLX
+A=CLOX
+A=CLOSX
+A=CLOSEX
+A=CLG
+A=CLGX
+A=CLG*
+A=C.
+A=CL.
+A=CLG.
+A=CX
+A=CLX
+A=CLGX
+A=CLS
+A=CLSX
+A=CLS*
+A=C.
+A=CL.
+A=CLS.
+A=CX
+A=CLX
+A=CLSX
+A=COS
+A=COSX
+A=COS*
+A=C.
+A=CO.
+A=COS.
+A=CX
+A=COX
+A=COSX
+A=COUNT
+A=COUNTX
+A=COUNT*
+A=C.
+A=CO.
+A=COU.
+A=COUN.
+A=COUNT.
+A=CX
+A=COX
+A=COUX
+A=COUNX
+A=COUNTX
+A=DATA
+A=DATAX
+A=DATA*
+A=D.
+A=DA.
+A=DAT.
+A=DATA.
+A=DX
+A=DAX
+A=DATX
+A=DATAX
+A=DEG
+A=DEGX
+A=DEG*
+A=D.
+A=DE.
+A=DEG.
+A=DX
+A=DEX
+A=DEGX
+A=DEF
+A=DEFX
+A=DEF*
+A=D.
+A=DE.
+A=DEF.
+A=DX
+A=DEX
+A=DEFX
+A=DELETE
+A=DELETEX
+A=DELETE*
+A=D.
+A=DE.
+A=DEL.
+A=DELE.
+A=DELET.
+A=DELETE.
+A=DX
+A=DEX
+A=DELX
+A=DELEX
+A=DELETX
+A=DELETEX
+A=DIV
+A=DIVX
+A=DIV*
+A=D.
+A=DI.
+A=DIV.
+A=DX
+A=DIX
+A=DIVX
+A=DIM
+A=DIMX
+A=DIM*
+A=D.
+A=DI.
+A=DIM.
+A=DX
+A=DIX
+A=DIMX
+A=DRAW
+A=DRAWX
+A=DRAW*
+A=D.
+A=DR.
+A=DRA.
+A=DRAW.
+A=DX
+A=DRX
+A=DRAX
+A=DRAWX
+A=ENDPROC
+A=ENDPROCX
+A=ENDPROC*
+A=E.
+A=EN.
+A=END.
+A=ENDP.
+A=ENDPR.
+A=ENDPRO.
+A=ENDPROC.
+A=EX
+A=ENX
+A=ENDX
+A=ENDPX
+A=ENDPRX
+A=ENDPROX
+A=ENDPROCX
+A=END
+A=ENDX
+A=END*
+A=E.
+A=EN.
+A=END.
+A=EX
+A=ENX
+A=ENDX
+A=ENVELOPE
+A=ENVELOPEX
+A=ENVELOPE*
+A=E.
+A=EN.
+A=ENV.
+A=ENVE.
+A=ENVEL.
+A=ENVELO.
+A=ENVELOP.
+A=ENVELOPE.
+A=EX
+A=ENX
+A=ENVX
+A=ENVEX
+A=ENVELX
+A=ENVELOX
+A=ENVELOPX
+A=ENVELOPEX
+A=ELSE
+A=ELSEX
+A=ELSE*
+A=E.
+A=EL.
+A=ELS.
+A=ELSE.
+A=EX
+A=ELX
+A=ELSX
+A=ELSEX
+A=EVAL
+A=EVALX
+A=EVAL*
+A=E.
+A=EV.
+A=EVA.
+A=EVAL.
+A=EX
+A=EVX
+A=EVAX
+A=EVALX
+A=ERL
+A=ERLX
+A=ERL*
+A=E.
+A=ER.
+A=ERL.
+A=EX
+A=ERX
+A=ERLX
+A=ERROR
+A=ERRORX
+A=ERROR*
+A=E.
+A=ER.
+A=ERR.
+A=ERRO.
+A=ERROR.
+A=EX
+A=ERX
+A=ERRX
+A=ERROX
+A=ERRORX
+A=EOF
+A=EOFX
+A=EOF*
+A=E.
+A=EO.
+A=EOF.
+A=EX
+A=EOX
+A=EOFX
+A=EOR
+A=EORX
+A=EOR*
+A=E.
+A=EO.
+A=EOR.
+A=EX
+A=EOX
+A=EORX
+A=ERR
+A=ERRX
+A=ERR*
+A=E.
+A=ER.
+A=ERR.
+A=EX
+A=ERX
+A=ERRX
+A=EXP
+A=EXPX
+A=EXP*
+A=E.
+A=EX.
+A=EXP.
+A=EX
+A=EXX
+A=EXPX
+A=EXT
+A=EXTX
+A=EXT*
+A=E.
+A=EX.
+A=EXT.
+A=EX
+A=EXX
+A=EXTX
+A=FOR
+A=FORX
+A=FOR*
+A=F.
+A=FO.
+A=FOR.
+A=FX
+A=FOX
+A=FORX
+A=FALSE
+A=FALSEX
+A=FALSE*
+A=F.
+A=FA.
+A=FAL.
+A=FALS.
+A=FALSE.
+A=FX
+A=FAX
+A=FALX
+A=FALSX
+A=FALSEX
+A=FN
+A=FNX
+A=FN*
+A=F.
+A=FN.
+A=FX
+A=FNX
+A=GOTO
+A=GOTOX
+A=GOTO*
+A=G.
+A=GO.
+A=GOT.
+A=GOTO.
+A=GX
+A=GOX
+A=GOTX
+A=GOTOX
+A=GET$
+A=GET$X
+A=GET$*
+A=G.
+A=GE.
+A=GET.
+A=GET$.
+A=GX
+A=GEX
+A=GETX
+A=GET$X
+A=GET
+A=GETX
+A=GET*
+A=G.
+A=GE.
+A=GET.
+A=GX
+A=GEX
+A=GETX
+A=GOSUB
+A=GOSUBX
+A=GOSUB*
+A=G.
+A=GO.
+A=GOS.
+A=GOSU.
+A=GOSUB.
+A=GX
+A=GOX
+A=GOSX
+A=GOSUX
+A=GOSUBX
+A=GCOL
+A=GCOLX
+A=GCOL*
+A=G.
+A=GC.
+A=GCO.
+A=GCOL.
+A=GX
+A=GCX
+A=GCOX
+A=GCOLX
+A=HIMEM
+A=HIMEMX
+A=HIMEM*
+A=H.
+A=HI.
+A=HIM.
+A=HIME.
+A=HIMEM.
+A=HX
+A=HIX
+A=HIMX
+A=HIMEX
+A=HIMEMX
+A=INPUT
+A=INPUTX
+A=INPUT*
+A=I.
+A=IN.
+A=INP.
+A=INPU.
+A=INPUT.
+A=IX
+A=INX
+A=INPX
+A=INPUX
+A=INPUTX
+A=IF
+A=IFX
+A=IF*
+A=I.
+A=IF.
+A=IX
+A=IFX
+A=INKEY$
+A=INKEY$X
+A=INKEY$*
+A=I.
+A=IN.
+A=INK.
+A=INKE.
+A=INKEY.
+A=INKEY$.
+A=IX
+A=INX
+A=INKX
+A=INKEX
+A=INKEYX
+A=INKEY$X
+A=INKEY
+A=INKEYX
+A=INKEY*
+A=I.
+A=IN.
+A=INK.
+A=INKE.
+A=INKEY.
+A=IX
+A=INX
+A=INKX
+A=INKEX
+A=INKEYX
+A=INT
+A=INTX
+A=INT*
+A=I.
+A=IN.
+A=INT.
+A=IX
+A=INX
+A=INTX
+A=INSTR(
+A=INSTR(X
+A=INSTR(*
+A=I.
+A=IN.
+A=INS.
+A=INST.
+A=INSTR.
+A=INSTR(.
+A=IX
+A=INX
+A=INSX
+A=INSTX
+A=INSTRX
+A=INSTR(X
+A=LIST
+A=LISTX
+A=LIST*
+A=L.
+A=LI.
+A=LIS.
+A=LIST.
+A=LX
+A=LIX
+A=LISX
+A=LISTX
+A=LINE
+A=LINEX
+A=LINE*
+A=L.
+A=LI.
+A=LIN.
+A=LINE.
+A=LX
+A=LIX
+A=LINX
+A=LINEX
+A=LOAD
+A=LOADX
+A=LOAD*
+A=L.
+A=LO.
+A=LOA.
+A=LOAD.
+A=LX
+A=LOX
+A=LOAX
+A=LOADX
+A=LOMEM
+A=LOMEMX
+A=LOMEM*
+A=L.
+A=LO.
+A=LOM.
+A=LOME.
+A=LOMEM.
+A=LX
+A=LOX
+A=LOMX
+A=LOMEX
+A=LOMEMX
+A=LOCAL
+A=LOCALX
+A=LOCAL*
+A=L.
+A=LO.
+A=LOC.
+A=LOCA.
+A=LOCAL.
+A=LX
+A=LOX
+A=LOCX
+A=LOCAX
+A=LOCALX
+A=LEFT$(
+A=LEFT$(X
+A=LEFT$(*
+A=L.
+A=LE.
+A=LEF.
+A=LEFT.
+A=LEFT$.
+A=LEFT$(.
+A=LX
+A=LEX
+A=LEFX
+A=LEFTX
+A=LEFT$X
+A=LEFT$(X
+A=LEN
+A=LENX
+A=LEN*
+A=L.
+A=LE.
+A=LEN.
+A=LX
+A=LEX
+A=LENX
+A=LET
+A=LETX
+A=LET*
+A=L.
+A=LE.
+A=LET.
+A=LX
+A=LEX
+A=LETX
+A=LOG
+A=LOGX
+A=LOG*
+A=L.
+A=LO.
+A=LOG.
+A=LX
+A=LOX
+A=LOGX
+A=LN
+A=LNX
+A=LN*
+A=L.
+A=LN.
+A=LX
+A=LNX
+A=MID$(
+A=MID$(X
+A=MID$(*
+A=M.
+A=MI.
+A=MID.
+A=MID$.
+A=MID$(.
+A=MX
+A=MIX
+A=MIDX
+A=MID$X
+A=MID$(X
+A=MODE
+A=MODEX
+A=MODE*
+A=M.
+A=MO.
+A=MOD.
+A=MODE.
+A=MX
+A=MOX
+A=MODX
+A=MODEX
+A=MOD
+A=MODX
+A=MOD*
+A=M.
+A=MO.
+A=MOD.
+A=MX
+A=MOX
+A=MODX
+A=MOVE
+A=MOVEX
+A=MOVE*
+A=M.
+A=MO.
+A=MOV.
+A=MOVE.
+A=MX
+A=MOX
+A=MOVX
+A=MOVEX
+A=NEXT
+A=NEXTX
+A=NEXT*
+A=N.
+A=NE.
+A=NEX.
+A=NEXT.
+A=NX
+A=NEX
+A=NEXX
+A=NEXTX
+A=NEW
+A=NEWX
+A=NEW*
+A=N.
+A=NE.
+A=NEW.
+A=NX
+A=NEX
+A=NEWX
+A=NOT
+A=NOTX
+A=NOT*
+A=N.
+A=NO.
+A=NOT.
+A=NX
+A=NOX
+A=NOTX
+A=OLD
+A=OLDX
+A=OLD*
+A=O.
+A=OL.
+A=OLD.
+A=OX
+A=OLX
+A=OLDX
+A=ON
+A=ONX
+A=ON*
+A=O.
+A=ON.
+A=OX
+A=ONX
+A=OFF
+A=OFFX
+A=OFF*
+A=O.
+A=OF.
+A=OFF.
+A=OX
+A=OFX
+A=OFFX
+A=OR
+A=ORX
+A=OR*
+A=O.
+A=OR.
+A=OX
+A=ORX
+A=OPENIN
+A=OPENINX
+A=OPENIN*
+A=O.
+A=OP.
+A=OPE.
+A=OPEN.
+A=OPENI.
+A=OPENIN.
+A=OX
+A=OPX
+A=OPEX
+A=OPENX
+A=OPENIX
+A=OPENINX
+A=OPENOUT
+A=OPENOUTX
+A=OPENOUT*
+A=O.
+A=OP.
+A=OPE.
+A=OPEN.
+A=OPENO.
+A=OPENOU.
+A=OPENOUT.
+A=OX
+A=OPX
+A=OPEX
+A=OPENX
+A=OPENOX
+A=OPENOUX
+A=OPENOUTX
+A=OPENUP
+A=OPENUPX
+A=OPENUP*
+A=O.
+A=OP.
+A=OPE.
+A=OPEN.
+A=OPENU.
+A=OPENUP.
+A=OX
+A=OPX
+A=OPEX
+A=OPENX
+A=OPENUX
+A=OPENUPX
+A=OSCLI
+A=OSCLIX
+A=OSCLI*
+A=O.
+A=OS.
+A=OSC.
+A=OSCL.
+A=OSCLI.
+A=OX
+A=OSX
+A=OSCX
+A=OSCLX
+A=OSCLIX
+A=PRINT
+A=PRINTX
+A=PRINT*
+A=P.
+A=PR.
+A=PRI.
+A=PRIN.
+A=PRINT.
+A=PX
+A=PRX
+A=PRIX
+A=PRINX
+A=PRINTX
+A=PAGE
+A=PAGEX
+A=PAGE*
+A=P.
+A=PA.
+A=PAG.
+A=PAGE.
+A=PX
+A=PAX
+A=PAGX
+A=PAGEX
+A=PTR
+A=PTRX
+A=PTR*
+A=P.
+A=PT.
+A=PTR.
+A=PX
+A=PTX
+A=PTRX
+A=PI
+A=PIX
+A=PI*
+A=P.
+A=PI.
+A=PX
+A=PIX
+A=PLOT
+A=PLOTX
+A=PLOT*
+A=P.
+A=PL.
+A=PLO.
+A=PLOT.
+A=PX
+A=PLX
+A=PLOX
+A=PLOTX
+A=POINT(
+A=POINT(X
+A=POINT(*
+A=P.
+A=PO.
+A=POI.
+A=POIN.
+A=POINT.
+A=POINT(.
+A=PX
+A=POX
+A=POIX
+A=POINX
+A=POINTX
+A=POINT(X
+A=PROC
+A=PROCX
+A=PROC*
+A=P.
+A=PR.
+A=PRO.
+A=PROC.
+A=PX
+A=PRX
+A=PROX
+A=PROCX
+A=POS
+A=POSX
+A=POS*
+A=P.
+A=PO.
+A=POS.
+A=PX
+A=POX
+A=POSX
+A=RETURN
+A=RETURNX
+A=RETURN*
+A=R.
+A=RE.
+A=RET.
+A=RETU.
+A=RETUR.
+A=RETURN.
+A=RX
+A=REX
+A=RETX
+A=RETUX
+A=RETURX
+A=RETURNX
+A=REPEAT
+A=REPEATX
+A=REPEAT*
+A=R.
+A=RE.
+A=REP.
+A=REPE.
+A=REPEA.
+A=REPEAT.
+A=RX
+A=REX
+A=REPX
+A=REPEX
+A=REPEAX
+A=REPEATX
+A=REPORT
+A=REPORTX
+A=REPORT*
+A=R.
+A=RE.
+A=REP.
+A=REPO.
+A=REPOR.
+A=REPORT.
+A=RX
+A=REX
+A=REPX
+A=REPOX
+A=REPORX
+A=REPORTX
+A=READ
+A=READX
+A=READ*
+A=R.
+A=RE.
+A=REA.
+A=READ.
+A=RX
+A=REX
+A=REAX
+A=READX
+A=REM
+A=REMX
+A=REM*
+A=R.
+A=RE.
+A=REM.
+A=RX
+A=REX
+A=REMX
+A=RUN
+A=RUNX
+A=RUN*
+A=R.
+A=RU.
+A=RUN.
+A=RX
+A=RUX
+A=RUNX
+A=RAD
+A=RADX
+A=RAD*
+A=R.
+A=RA.
+A=RAD.
+A=RX
+A=RAX
+A=RADX
+A=RESTORE
+A=RESTOREX
+A=RESTORE*
+A=R.
+A=RE.
+A=RES.
+A=REST.
+A=RESTO.
+A=RESTOR.
+A=RESTORE.
+A=RX
+A=REX
+A=RESX
+A=RESTX
+A=RESTOX
+A=RESTORX
+A=RESTOREX
+A=RIGHT$(
+A=RIGHT$(X
+A=RIGHT$(*
+A=R.
+A=RI.
+A=RIG.
+A=RIGH.
+A=RIGHT.
+A=RIGHT$.
+A=RIGHT$(.
+A=RX
+A=RIX
+A=RIGX
+A=RIGHX
+A=RIGHTX
+A=RIGHT$X
+A=RIGHT$(X
+A=RND
+A=RNDX
+A=RND*
+A=R.
+A=RN.
+A=RND.
+A=RX
+A=RNX
+A=RNDX
+A=RENUMBER
+A=RENUMBERX
+A=RENUMBER*
+A=R.
+A=RE.
+A=REN.
+A=RENU.
+A=RENUM.
+A=RENUMB.
+A=RENUMBE.
+A=RENUMBER.
+A=RX
+A=REX
+A=RENX
+A=RENUX
+A=RENUMX
+A=RENUMBX
+A=RENUMBEX
+A=RENUMBERX
+A=STEP
+A=STEPX
+A=STEP*
+A=S.
+A=ST.
+A=STE.
+A=STEP.
+A=SX
+A=STX
+A=STEX
+A=STEPX
+A=SAVE
+A=SAVEX
+A=SAVE*
+A=S.
+A=SA.
+A=SAV.
+A=SAVE.
+A=SX
+A=SAX
+A=SAVX
+A=SAVEX
+A=SGN
+A=SGNX
+A=SGN*
+A=S.
+A=SG.
+A=SGN.
+A=SX
+A=SGX
+A=SGNX
+A=SIN
+A=SINX
+A=SIN*
+A=S.
+A=SI.
+A=SIN.
+A=SX
+A=SIX
+A=SINX
+A=SQR
+A=SQRX
+A=SQR*
+A=S.
+A=SQ.
+A=SQR.
+A=SX
+A=SQX
+A=SQRX
+A=SPC
+A=SPCX
+A=SPC*
+A=S.
+A=SP.
+A=SPC.
+A=SX
+A=SPX
+A=SPCX
+A=STR$
+A=STR$X
+A=STR$*
+A=S.
+A=ST.
+A=STR.
+A=STR$.
+A=SX
+A=STX
+A=STRX
+A=STR$X
+A=STRING$(
+A=STRING$(X
+A=STRING$(*
+A=S.
+A=ST.
+A=STR.
+A=STRI.
+A=STRIN.
+A=STRING.
+A=STRING$.
+A=STRING$(.
+A=SX
+A=STX
+A=STRX
+A=STRIX
+A=STRINX
+A=STRINGX
+A=STRING$X
+A=STRING$(X
+A=SOUND
+A=SOUNDX
+A=SOUND*
+A=S.
+A=SO.
+A=SOU.
+A=SOUN.
+A=SOUND.
+A=SX
+A=SOX
+A=SOUX
+A=SOUNX
+A=SOUNDX
+A=STOP
+A=STOPX
+A=STOP*
+A=S.
+A=ST.
+A=STO.
+A=STOP.
+A=SX
+A=STX
+A=STOX
+A=STOPX
+A=TAN
+A=TANX
+A=TAN*
+A=T.
+A=TA.
+A=TAN.
+A=TX
+A=TAX
+A=TANX
+A=THEN
+A=THENX
+A=THEN*
+A=T.
+A=TH.
+A=THE.
+A=THEN.
+A=TX
+A=THX
+A=THEX
+A=THENX
+A=TO
+A=TOX
+A=TO*
+A=T.
+A=TO.
+A=TX
+A=TOX
+A=TAB(
+A=TAB(X
+A=TAB(*
+A=T.
+A=TA.
+A=TAB.
+A=TAB(.
+A=TX
+A=TAX
+A=TABX
+A=TAB(X
+A=TRACE
+A=TRACEX
+A=TRACE*
+A=T.
+A=TR.
+A=TRA.
+A=TRAC.
+A=TRACE.
+A=TX
+A=TRX
+A=TRAX
+A=TRACX
+A=TRACEX
+A=TIME
+A=TIMEX
+A=TIME*
+A=T.
+A=TI.
+A=TIM.
+A=TIME.
+A=TX
+A=TIX
+A=TIMX
+A=TIMEX
+A=TRUE
+A=TRUEX
+A=TRUE*
+A=T.
+A=TR.
+A=TRU.
+A=TRUE.
+A=TX
+A=TRX
+A=TRUX
+A=TRUEX
+A=UNTIL
+A=UNTILX
+A=UNTIL*
+A=U.
+A=UN.
+A=UNT.
+A=UNTI.
+A=UNTIL.
+A=UX
+A=UNX
+A=UNTX
+A=UNTIX
+A=UNTILX
+A=USR
+A=USRX
+A=USR*
+A=U.
+A=US.
+A=USR.
+A=UX
+A=USX
+A=USRX
+A=VDU
+A=VDUX
+A=VDU*
+A=V.
+A=VD.
+A=VDU.
+A=VX
+A=VDX
+A=VDUX
+A=VAL
+A=VALX
+A=VAL*
+A=V.
+A=VA.
+A=VAL.
+A=VX
+A=VAX
+A=VALX
+A=VPOS
+A=VPOSX
+A=VPOS*
+A=V.
+A=VP.
+A=VPO.
+A=VPOS.
+A=VX
+A=VPX
+A=VPOX
+A=VPOSX
+A=WIDTH
+A=WIDTHX
+A=WIDTH*
+A=W.
+A=WI.
+A=WID.
+A=WIDT.
+A=WIDTH.
+A=WX
+A=WIX
+A=WIDX
+A=WIDTX
+A=WIDTHX
+A=PAGE
+A=PAGEX
+A=PAGE*
+A=P.
+A=PA.
+A=PAG.
+A=PAGE.
+A=PX
+A=PAX
+A=PAGX
+A=PAGEX
+A=PTR
+A=PTRX
+A=PTR*
+A=P.
+A=PT.
+A=PTR.
+A=PX
+A=PTX
+A=PTRX
+A=TIME
+A=TIMEX
+A=TIME*
+A=T.
+A=TI.
+A=TIM.
+A=TIME.
+A=TX
+A=TIX
+A=TIMX
+A=TIMEX
+A=LOMEM
+A=LOMEMX
+A=LOMEM*
+A=L.
+A=LO.
+A=LOM.
+A=LOME.
+A=LOMEM.
+A=LX
+A=LOX
+A=LOMX
+A=LOMEX
+A=LOMEMX
+A=HIMEM
+A=HIMEMX
+A=HIMEM*
+A=H.
+A=HI.
+A=HIM.
+A=HIME.
+A=HIMEM.
+A=HX
+A=HIX
+A=HIMX
+A=HIMEX
+A=HIMEMX
diff --git a/test/3-directives/putbasic/abbreviations.gold.ssd b/test/3-directives/putbasic/abbreviations.gold.ssd
new file mode 100644
index 0000000..e5455d3
Binary files /dev/null and b/test/3-directives/putbasic/abbreviations.gold.ssd differ
diff --git a/test/3-directives/putbasic/issue-62.6502 b/test/3-directives/putbasic/issue-62.6502
new file mode 100644
index 0000000..c8b72f3
--- /dev/null
+++ b/test/3-directives/putbasic/issue-62.6502
@@ -0,0 +1 @@
+PUTBASIC "issue-62.bas", "$.ISS-62"
diff --git a/test/3-directives/putbasic/issue-62.bas b/test/3-directives/putbasic/issue-62.bas
new file mode 100644
index 0000000..9936322
--- /dev/null
+++ b/test/3-directives/putbasic/issue-62.bas
@@ -0,0 +1,2 @@
+0 REM Start at line zero
+1 REM And add a line one
diff --git a/test/3-directives/putbasic/issue-62.gold.ssd b/test/3-directives/putbasic/issue-62.gold.ssd
new file mode 100644
index 0000000..446e0b7
Binary files /dev/null and b/test/3-directives/putbasic/issue-62.gold.ssd differ
diff --git a/test/3-directives/putbasic/issue-63.6502 b/test/3-directives/putbasic/issue-63.6502
new file mode 100644
index 0000000..6b626a7
--- /dev/null
+++ b/test/3-directives/putbasic/issue-63.6502
@@ -0,0 +1 @@
+PUTBASIC "issue-63.bas", "$.ISS-63"
diff --git a/test/3-directives/putbasic/issue-63.bas b/test/3-directives/putbasic/issue-63.bas
new file mode 100644
index 0000000..9a3fb75
--- /dev/null
+++ b/test/3-directives/putbasic/issue-63.bas
@@ -0,0 +1,2 @@
+10 REM Tokenizing of PAGE on LHS
+20 ?(PAGE+1024)=1
diff --git a/test/3-directives/putbasic/issue-63.gold.ssd b/test/3-directives/putbasic/issue-63.gold.ssd
new file mode 100644
index 0000000..ae89a58
Binary files /dev/null and b/test/3-directives/putbasic/issue-63.gold.ssd differ
diff --git a/test/3-directives/putbasic/issue-65.6502 b/test/3-directives/putbasic/issue-65.6502
new file mode 100644
index 0000000..417fb91
--- /dev/null
+++ b/test/3-directives/putbasic/issue-65.6502
@@ -0,0 +1 @@
+PUTBASIC "issue-65.bas", "$.ISS-65"
diff --git a/test/3-directives/putbasic/issue-65.bas b/test/3-directives/putbasic/issue-65.bas
new file mode 100644
index 0000000..ed9b0f2
--- /dev/null
+++ b/test/3-directives/putbasic/issue-65.bas
@@ -0,0 +1,2 @@
+1REM Should not require a space before the MOD
+2PROCADJUST(1,(?&276BMOD16)+10*(?&276B DIV16))
diff --git a/test/3-directives/putbasic/issue-65.gold.ssd b/test/3-directives/putbasic/issue-65.gold.ssd
new file mode 100644
index 0000000..94ef45a
Binary files /dev/null and b/test/3-directives/putbasic/issue-65.gold.ssd differ
diff --git a/test/3-directives/putbasic/pound.6502 b/test/3-directives/putbasic/pound.6502
new file mode 100644
index 0000000..ef01824
--- /dev/null
+++ b/test/3-directives/putbasic/pound.6502
@@ -0,0 +1 @@
+PUTBASIC "pound.bas", "$.POUND"
diff --git a/test/3-directives/putbasic/pound.bas b/test/3-directives/putbasic/pound.bas
new file mode 100644
index 0000000..d3f8979
--- /dev/null
+++ b/test/3-directives/putbasic/pound.bas
@@ -0,0 +1,8 @@
+10 END`EOR=1
+20 COUNT_MOD=2
+30 `PRINT=3
+40 _END=4
+50 PRINTEND`EOR
+60 PRINTCOUNT_MOD
+70 PRINT`PRINT
+80 PRINT_END
diff --git a/test/3-directives/putbasic/pound.gold.ssd b/test/3-directives/putbasic/pound.gold.ssd
new file mode 100644
index 0000000..6f32347
Binary files /dev/null and b/test/3-directives/putbasic/pound.gold.ssd differ