beebasm

changeset 32:ceed0131ba5d

Forgot to add the goddamn files!
author RichTW <richtw1@gmail.com>
date Tue Mar 01 20:05:48 2011 +0100
parents eb7f757ddafa
children 58954bd1a2d9
files src/BASIC.cpp src/BASIC.h
diffstat 2 files changed, 833 insertions(+), 0 deletions(-) [+]
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/BASIC.cpp	Tue Mar 01 20:05:48 2011 +0100
     1.3 @@ -0,0 +1,794 @@
     1.4 +/*************************************************************************************************/
     1.5 +/**
     1.6 +	BASIC.cpp
     1.7 +
     1.8 +	Contains routines for tokenising/detokenising BBC BASIC programs.
     1.9 +
    1.10 +	Modified from code by Thomas Harte.
    1.11 +
    1.12 +	Copyright (C) Thomas Harte
    1.13 +
    1.14 +	This file is part of BeebAsm.
    1.15 +
    1.16 +	BeebAsm is free software: you can redistribute it and/or modify it under the terms of the GNU
    1.17 +	General Public License as published by the Free Software Foundation, either version 3 of the
    1.18 +	License, or (at your option) any later version.
    1.19 +
    1.20 +	BeebAsm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
    1.21 +	even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.22 +	GNU General Public License for more details.
    1.23 +
    1.24 +	You should have received a copy of the GNU General Public License along with BeebAsm, as
    1.25 +	COPYING.txt.  If not, see <http://www.gnu.org/licenses/>.
    1.26 +*/
    1.27 +/*************************************************************************************************/
    1.28 +
    1.29 +#include "BASIC.h"
    1.30 +#include <stdio.h>
    1.31 +#include <string.h>
    1.32 +#include <stdlib.h>
    1.33 +
    1.34 +/*
    1.35 +
    1.36 +	KeyWord table - used by both the exporter and the importer to convert
    1.37 +	between the plain text version of a BASIC program and the format used
    1.38 +	internally by the BASIC ROM
    1.39 +
    1.40 +	Information taken from pages 41-43 of the BASIC ROM User Guide, 
    1.41 +	BASIC 2 ROM assumed throughout
    1.42 +
    1.43 +*/
    1.44 +
    1.45 +struct KeyWord
    1.46 +{
    1.47 +	KeyWord(const char* name, Uint8 flags)
    1.48 +		: Name(name), Flags(flags), StrLen(0), Next(NULL)
    1.49 +	{}
    1.50 +
    1.51 +	const char *Name;
    1.52 +	Uint8 Flags;
    1.53 +	unsigned int StrLen;
    1.54 +	struct KeyWord *Next;
    1.55 +};
    1.56 +
    1.57 +struct KeyWord KeyWordTable[0x80] =
    1.58 +{
    1.59 +	/* 0x80 */
    1.60 +	KeyWord("AND",	0x00),		KeyWord("DIV",	0x00),		KeyWord("EOR",	0x00),		KeyWord("MOD",	0x00),
    1.61 +	KeyWord("OR",	0x00),		KeyWord("ERROR",0x04),		KeyWord("LINE",	0x00),		KeyWord("OFF",	0x00),
    1.62 +
    1.63 +	/* 0x88 */
    1.64 +	KeyWord("STEP",	0x00),		KeyWord("SPC",	0x00),		KeyWord("TAB(",	0x00),		KeyWord("ELSE",	0x14),
    1.65 +	KeyWord("THEN",	0x14),		KeyWord("",		0x00),		KeyWord("OPENIN",0x00),		KeyWord("PTR",	0x43),
    1.66 +
    1.67 +	/* 0x90 */
    1.68 +	KeyWord("PAGE",	0x43),		KeyWord("TIME",	0x43),		KeyWord("LOMEM",0x43),		KeyWord("HIMEM",0x43),
    1.69 +	KeyWord("ABS",	0x00),		KeyWord("ACS",	0x00),		KeyWord("ADVAL",0x00),		KeyWord("ASC",	0x00),
    1.70 +
    1.71 +	/* 0x98 */
    1.72 +	KeyWord("ASN",	0x00),		KeyWord("ATN",	0x00),		KeyWord("BGET",	0x01),		KeyWord("COS",	0x00),
    1.73 +	KeyWord("COUNT",0x01),		KeyWord("DEG",	0x00),		KeyWord("ERL",	0x01),		KeyWord("ERR",	0x01),
    1.74 +
    1.75 +	/* 0xa0 */
    1.76 +	KeyWord("EVAL",	0x00),		KeyWord("EXP",	0x00),		KeyWord("EXT",	0x01),		KeyWord("FALSE",0x01),
    1.77 +	KeyWord("FN",	0x08),		KeyWord("GET",	0x00),		KeyWord("INKEY",0x00),		KeyWord("INSTR(",0x00),
    1.78 +
    1.79 +	/* 0xa8 */
    1.80 +	KeyWord("INT",	0x00),		KeyWord("LEN",	0x00),		KeyWord("LN",	0x00),		KeyWord("LOG",	0x00),
    1.81 +	KeyWord("NOT",	0x00),		KeyWord("OPENUP",0x00),		KeyWord("OPENOUT",0x00),	KeyWord("PI",	0x01),
    1.82 +
    1.83 +	/* 0xb0 */
    1.84 +	KeyWord("POINT(",0x00),		KeyWord("POS",	0x01),		KeyWord("RAD",	0x00),		KeyWord("RND",	0x01),
    1.85 +	KeyWord("SGN",	0x00),		KeyWord("SIN",	0x00),		KeyWord("SQR",	0x00),		KeyWord("TAN",	0x00),
    1.86 +
    1.87 +	/* 0xb8 */
    1.88 +	KeyWord("TO",	0x00),		KeyWord("TRUE",	0x01),		KeyWord("USR",	0x00),		KeyWord("VAL",	0x00),
    1.89 +	KeyWord("VPOS",	0x01),		KeyWord("CHR$",	0x00),		KeyWord("GET$",	0x00),		KeyWord("INKEY$",0x00),
    1.90 +
    1.91 +	/* 0xc0 */
    1.92 +	KeyWord("LEFT$(",0x00),		KeyWord("MID$(",0x00),		KeyWord("RIGHT$(",0x00),	KeyWord("STR$",	0x00),
    1.93 +	KeyWord("STRING$(",0x00),	KeyWord("EOF",	0x01),		KeyWord("AUTO",	0x10),		KeyWord("DELETE",0x10),
    1.94 +
    1.95 +	/* 0xc8 */
    1.96 +	KeyWord("LOAD",	0x02),		KeyWord("LIST",	0x10),		KeyWord("NEW",	0x01),		KeyWord("OLD",	0x01),
    1.97 +	KeyWord("RENUMBER",0x10),	KeyWord("SAVE",	0x02),		KeyWord("",		0x00),		KeyWord("PTR",	0x00),
    1.98 +
    1.99 +	/* 0xd0 */
   1.100 +	KeyWord("PAGE",	0x00),		KeyWord("TIME",	0x01),		KeyWord("LOMEM",0x00),		KeyWord("HIMEM",0x00),
   1.101 +	KeyWord("SOUND",0x02),		KeyWord("BPUT",	0x03),		KeyWord("CALL",	0x02),		KeyWord("CHAIN",0x02),
   1.102 +
   1.103 +	/* 0xd8 */
   1.104 +	KeyWord("CLEAR",0x01),		KeyWord("CLOSE",0x03),		KeyWord("CLG",	0x01),		KeyWord("CLS",	0x01),
   1.105 +	KeyWord("DATA",	0x20),		KeyWord("DEF",	0x00),		KeyWord("DIM",	0x02),		KeyWord("DRAW",	0x02),
   1.106 +
   1.107 +	/* 0xe0 */
   1.108 +	KeyWord("END",	0x01),		KeyWord("ENDPROC",0x01),	KeyWord("ENVELOPE",0x02),	KeyWord("FOR",	0x02),
   1.109 +	KeyWord("GOSUB",0x12),		KeyWord("GOTO",	0x12),		KeyWord("GCOL",	0x02),		KeyWord("IF",	0x02),
   1.110 +
   1.111 +	/* 0xe8 */
   1.112 +	KeyWord("INPUT",0x02),		KeyWord("LET",	0x04),		KeyWord("LOCAL",0x02),		KeyWord("MODE",	0x02),
   1.113 +	KeyWord("MOVE",	0x02),		KeyWord("NEXT",	0x02),		KeyWord("ON",	0x02),		KeyWord("VDU",	0x02),
   1.114 +
   1.115 +	/* 0xf0 */
   1.116 +	KeyWord("PLOT",	0x02),		KeyWord("PRINT",0x02),		KeyWord("PROC",	0x0a),		KeyWord("READ",	0x02),
   1.117 +	KeyWord("REM",	0x20),		KeyWord("REPEAT",0x00),		KeyWord("REPORT",0x01),		KeyWord("RESTORE",0x12),
   1.118 +
   1.119 +	/* 0xf8 */
   1.120 +	KeyWord("RETURN",0x01),		KeyWord("RUN",	0x01),		KeyWord("STOP",	0x01),		KeyWord("COLOUR",0x02),
   1.121 +	KeyWord("TRACE",0x12),		KeyWord("UNTIL",0x02),		KeyWord("WIDTH",0x02),		KeyWord("OSCLI",0x02)
   1.122 +};
   1.123 +
   1.124 +KeyWord *QuickTable[26*26];
   1.125 +
   1.126 +/*
   1.127 +
   1.128 +	Setup function, to establish contents of QuickTable, store strlens, etc
   1.129 +
   1.130 +*/
   1.131 +
   1.132 +#define HashCode(str)	(str[0] < 'A' || str[0] > 'Z' || str[1] < 'A' || str[1] > 'Z') ? 0 : ((str[0] - 'A')*26 + (str[1] - 'A'))
   1.133 +
   1.134 +void SetupBASICTables()
   1.135 +{
   1.136 +	/* set QuickTable to empty */
   1.137 +	int c = 26*26;
   1.138 +	while(c--)
   1.139 +		QuickTable[c] = NULL;
   1.140 +
   1.141 +	/* go through tokens, store strlens & populate QuickTable */
   1.142 +	for(c = 0; c < 0x80; c++)
   1.143 +	{
   1.144 +		if((KeyWordTable[c].StrLen = strlen(KeyWordTable[c].Name)))
   1.145 +		{
   1.146 +			/* reject any symbols that have already appeared 0x40 places earlier in the table */
   1.147 +			if(c < 0x40 || strcmp(KeyWordTable[c].Name, KeyWordTable[c - 0x40].Name))
   1.148 +			{
   1.149 +				int Code = HashCode(KeyWordTable[c].Name);
   1.150 +				KeyWord **InsertPointer = &QuickTable[Code];
   1.151 +				while(*InsertPointer)
   1.152 +					InsertPointer = &(*InsertPointer)->Next;
   1.153 +
   1.154 +				*InsertPointer = &KeyWordTable[c];
   1.155 +			}
   1.156 +		}
   1.157 +	}
   1.158 +
   1.159 +	/*
   1.160 +
   1.161 +		Go through QuickTable, sorting each branch by string length
   1.162 +
   1.163 +		I'm an idiot, so I've used insertion sort!
   1.164 +
   1.165 +	*/
   1.166 +	c = 26*26;
   1.167 +	while(c--)
   1.168 +		if(QuickTable[c] && QuickTable[c]->Next)
   1.169 +		{
   1.170 +			/* sort first by string length */
   1.171 +			KeyWord **Check = &QuickTable[c];
   1.172 +			unsigned int CurLength = (*Check)->StrLen;
   1.173 +			Check = &(*Check)->Next;
   1.174 +			while(*Check)
   1.175 +			{
   1.176 +				/* check if out of order */
   1.177 +				if((*Check)->StrLen > CurLength)
   1.178 +				{
   1.179 +					/* unlink */
   1.180 +					KeyWord *Takeout = *Check;
   1.181 +					*Check = (*Check)->Next;
   1.182 +
   1.183 +					/* start at top of list, find correct insertion point */
   1.184 +					KeyWord **InsertPoint = &QuickTable[c];
   1.185 +					while((*InsertPoint)->StrLen >= Takeout->StrLen)
   1.186 +						InsertPoint = &(*InsertPoint)->Next;
   1.187 +
   1.188 +					/* ...and insert */
   1.189 +					Takeout->Next = *InsertPoint;
   1.190 +					*InsertPoint = Takeout;
   1.191 +				}
   1.192 +				else
   1.193 +				{
   1.194 +					CurLength = (*Check)->StrLen;
   1.195 +					Check = &(*Check)->Next;
   1.196 +				}
   1.197 +			}
   1.198 +		}
   1.199 +}
   1.200 +
   1.201 +/*
   1.202 +
   1.203 +	Little function to return an error string
   1.204 +
   1.205 +*/
   1.206 +const char *ErrorTable[] =
   1.207 +{
   1.208 +	"",
   1.209 +	"BASIC is not currently active",
   1.210 +	"Unable to open file for input",
   1.211 +	"Program too large",
   1.212 +	"Unable to open file for output",
   1.213 +	"Malformed BASIC program or not running BASIC",
   1.214 +	"BASIC program appears to run past the end of RAM"
   1.215 +};
   1.216 +char DynamicErrorText[256];
   1.217 +
   1.218 +int ErrorNum;
   1.219 +
   1.220 +const char *GetBASICError()
   1.221 +{
   1.222 +	return ErrorNum >= 0 ? ErrorTable[ErrorNum] : DynamicErrorText;
   1.223 +}
   1.224 +
   1.225 +int GetBASICErrorNum()
   1.226 +{
   1.227 +	return ErrorNum;
   1.228 +}
   1.229 +
   1.230 +
   1.231 +/*
   1.232 +
   1.233 +	Functions to export BASIC code, i.e. decode from tokenised form to plain text
   1.234 +
   1.235 +*/
   1.236 +
   1.237 +bool ExtractLine(FILE *output, Uint8 *Memory, Uint16 Addr, Uint8 LineL)
   1.238 +{
   1.239 +	int LineLength = static_cast<int>(LineL);
   1.240 +
   1.241 +	while(LineLength >= 0)
   1.242 +	{
   1.243 +		Uint8 ThisByte = Memory[Addr]; Addr++; LineLength--;
   1.244 +		if(ThisByte >= 0x80)
   1.245 +		{
   1.246 +			if(ThisByte == 0x8d) // then we're about to see a tokenised line number
   1.247 +			{
   1.248 +				Uint16 LineNumber;
   1.249 +
   1.250 +				// decode weirdo tokenised line number format
   1.251 +				LineNumber = Memory[Addr+1]&0x3f;
   1.252 +				LineNumber |= (Memory[Addr+2]&0x3f) << 8;
   1.253 +				LineNumber |= (Memory[Addr]&0x0c) << 12;
   1.254 +				LineNumber |= (Memory[Addr]&0x30) << 2;
   1.255 +				LineNumber ^= 0x4040;
   1.256 +
   1.257 +				Addr += 3;
   1.258 +				LineLength -= 3;
   1.259 +
   1.260 +				fprintf(output, "%d", LineNumber);
   1.261 +			}
   1.262 +			else //ordinary keyword
   1.263 +			{
   1.264 +				fputs(KeyWordTable[ThisByte - 0x80].Name, output);
   1.265 +				
   1.266 +				if(KeyWordTable[ThisByte - 0x80].Flags & 0x20)
   1.267 +				{
   1.268 +					//copy to end of line without interpreting tokens}
   1.269 +					while(LineLength >= 0)
   1.270 +					{
   1.271 +						fputc(Memory[Addr], output); Addr++; LineLength--;
   1.272 +					}
   1.273 +					return true;
   1.274 +				}
   1.275 +			}
   1.276 +		}
   1.277 +		else
   1.278 +		{
   1.279 +			switch(ThisByte)
   1.280 +			{
   1.281 +				default: fputc(ThisByte, output); break;
   1.282 +				case '"':
   1.283 +					/* copy string literal... */
   1.284 +					fputc('"', output);
   1.285 +					while(Memory[Addr] != '"' && LineLength >= 0)
   1.286 +					{
   1.287 +						fputc(Memory[Addr], output);
   1.288 +						Addr++; LineLength--;
   1.289 +					}
   1.290 +					if(Memory[Addr] == '"')
   1.291 +					{
   1.292 +						fputc(Memory[Addr], output);
   1.293 +						Addr++; LineLength--;
   1.294 +					}
   1.295 +				break;
   1.296 +			}
   1.297 +		}
   1.298 +	}
   1.299 +
   1.300 +	return (LineLength == -1) ? true : false;
   1.301 +}
   1.302 +
   1.303 +bool ExportBASIC(const char *Filename, Uint8 *Memory)
   1.304 +{
   1.305 +	ErrorNum = 0;
   1.306 +	FILE *output = fopen(Filename, "wt");
   1.307 +
   1.308 +	if(!output)
   1.309 +	{
   1.310 +		ErrorNum = 4;
   1.311 +		return false;
   1.312 +	}
   1.313 +
   1.314 +	/* get the value of PAGE‚ start reading BASIC code from there */
   1.315 +	Uint16 Addr = Memory[0x18] << 8;
   1.316 +
   1.317 +	if(Addr >= 32768 - 4)
   1.318 +		ErrorNum = 6;
   1.319 +
   1.320 +	while(!ErrorNum)
   1.321 +	{
   1.322 +		/* character here should be \r */
   1.323 +		if(Memory[Addr] != 0x0d)
   1.324 +		{
   1.325 +			ErrorNum = 5;
   1.326 +			break;
   1.327 +		}
   1.328 +		Addr++;
   1.329 +
   1.330 +		/* get line number, check if we've hit the end of BASIC */
   1.331 +		Uint16 LineNumber;
   1.332 +		LineNumber = (Memory[Addr] << 8) | Memory[Addr+1];
   1.333 +		Addr += 2;
   1.334 +		if(LineNumber & 0x8000) // if we've hit the end of the program, exit
   1.335 +			break;
   1.336 +
   1.337 +		Uint8 LineLength = Memory[Addr]; Addr++;
   1.338 +
   1.339 +		if(Addr+LineLength >= 32768 - 4)
   1.340 +		{
   1.341 +			ErrorNum = 6;
   1.342 +			break;
   1.343 +		}
   1.344 +
   1.345 +		/* print line number */
   1.346 +		fprintf(output, "%5d", LineNumber);
   1.347 +
   1.348 +		/* detokenise, etc */
   1.349 +		if(!ExtractLine(output, Memory, Addr, LineLength - 4))
   1.350 +			break;
   1.351 +
   1.352 +		/* add a newline */
   1.353 +		fputc('\n', output);
   1.354 +
   1.355 +		/* should process line here, but chicken out */
   1.356 +		Addr += LineLength - 4;
   1.357 +	}
   1.358 +
   1.359 +	fclose(output);
   1.360 +
   1.361 +	return ErrorNum ? false : true;
   1.362 +}
   1.363 +
   1.364 +/*
   1.365 +
   1.366 +	Functions to import BASIC code, i.e. tokenise from plain text
   1.367 +
   1.368 +*/
   1.369 +
   1.370 +#define AlphaNumeric(v)\
   1.371 +						(\
   1.372 +							(v >= 'a' && v <= 'z') ||\
   1.373 +							(v >= 'A' && v <= 'Z') ||\
   1.374 +							(v >= '0' && v <= '9')\
   1.375 +						)
   1.376 +
   1.377 +char IncomingBuffer[9];
   1.378 +Uint8 Token, NextChar;
   1.379 +unsigned int IncomingPointer;
   1.380 +FILE *inputfile;
   1.381 +bool EndOfFile, NumberStart;
   1.382 +unsigned int NumberValue, NumberLength;
   1.383 +int CurLine;
   1.384 +
   1.385 +Uint8 *Memory;
   1.386 +Uint16 Addr;
   1.387 +
   1.388 +inline bool WriteByte(Uint8 value)
   1.389 +{
   1.390 +	if(Addr == 32768) {ErrorNum = 3; return false;}
   1.391 +	Memory[Addr++] = value;
   1.392 +	return true;
   1.393 +}
   1.394 +
   1.395 +int my_fgetc(FILE *in)
   1.396 +{
   1.397 +	int r;
   1.398 +	do
   1.399 +	{
   1.400 +		r = fgetc(in);
   1.401 +	}
   1.402 +	while(r == '\r');
   1.403 +	if(r == '\n') CurLine++;
   1.404 +	return r;
   1.405 +}
   1.406 +
   1.407 +void GetCharacter()
   1.408 +{
   1.409 +	if(IncomingPointer == 8)
   1.410 +	{
   1.411 +		/* shift, load into position [8] */
   1.412 +		int c = 0;
   1.413 +		while(c < 8)
   1.414 +		{
   1.415 +			IncomingBuffer[c] = IncomingBuffer[c+1];
   1.416 +			c++;
   1.417 +		}
   1.418 +
   1.419 +		IncomingBuffer[8] = my_fgetc(inputfile);
   1.420 +	}
   1.421 +	else
   1.422 +	{
   1.423 +		IncomingBuffer[IncomingPointer] = my_fgetc(inputfile);
   1.424 +		IncomingPointer++;
   1.425 +	}
   1.426 +
   1.427 +	if(feof(inputfile)) //if we've hit feof then the last char isn't anything
   1.428 +	{
   1.429 +		IncomingPointer--;
   1.430 +		if(!IncomingPointer)
   1.431 +		{
   1.432 +			EndOfFile = true;
   1.433 +			return;
   1.434 +		}
   1.435 +	}
   1.436 +
   1.437 +	/* check for tokens, set flags accordingly. Be a bit dense about this for now! */
   1.438 +	Token = IncomingBuffer[0];
   1.439 +	int Code = HashCode(IncomingBuffer);
   1.440 +	KeyWord *CheckPtr = QuickTable[Code];
   1.441 +
   1.442 +	while(CheckPtr)
   1.443 +	{
   1.444 +		if(IncomingPointer >= CheckPtr->StrLen && !strncmp(IncomingBuffer, CheckPtr->Name, CheckPtr->StrLen))
   1.445 +		{
   1.446 +			Token = (CheckPtr - KeyWordTable) + 0x80;
   1.447 +			NextChar = IncomingBuffer[CheckPtr->StrLen];
   1.448 +			break;
   1.449 +		}
   1.450 +
   1.451 +		CheckPtr = CheckPtr->Next;
   1.452 +	}
   1.453 +
   1.454 +	/* check if this is a number start */
   1.455 +	NumberStart = false;
   1.456 +	if(Token >= '0' && Token <= '9')
   1.457 +	{
   1.458 +		NumberStart = true;
   1.459 +		char *end;
   1.460 +		NumberValue = strtol(IncomingBuffer, &end, 10);
   1.461 +		NumberLength = end - IncomingBuffer;
   1.462 +	}
   1.463 +}
   1.464 +
   1.465 +void EatCharacters(int n)
   1.466 +{
   1.467 +	/* shift left n places, decrease IncomingPointer */
   1.468 +	int c = 0;
   1.469 +	while(c < 9-n)
   1.470 +	{
   1.471 +		IncomingBuffer[c] = IncomingBuffer[c+n];
   1.472 +		c++;
   1.473 +	}
   1.474 +	IncomingPointer -= n;
   1.475 +	IncomingBuffer[IncomingPointer] = '\0';
   1.476 +	while(n--)		/* this is a quick fix: it causes lots of unnecessary token searches... */
   1.477 +		GetCharacter();
   1.478 +}
   1.479 +
   1.480 +bool CopyStringLiteral()
   1.481 +{
   1.482 +	// eat preceeding quote
   1.483 +	WriteByte(IncomingBuffer[0]);
   1.484 +	EatCharacters(1);
   1.485 +
   1.486 +	// don't tokenise anything until another quote is hit, keep eye out for things that may have gone wrong
   1.487 +	while(!ErrorNum && !EndOfFile && IncomingBuffer[0] != '"' && IncomingBuffer[0] != '\n')
   1.488 +	{
   1.489 +		WriteByte(IncomingBuffer[0]);
   1.490 +		EatCharacters(1);
   1.491 +	}
   1.492 +
   1.493 +	if(IncomingBuffer[0] != '"') // stopped going for some reason other than a close quote
   1.494 +	{
   1.495 +		ErrorNum = -1;
   1.496 +		sprintf(DynamicErrorText, "Malformed string literal on line %d", CurLine);
   1.497 +		return false;
   1.498 +	}
   1.499 +
   1.500 +	// eat proceeding quote
   1.501 +	WriteByte(IncomingBuffer[0]);
   1.502 +	EatCharacters(1);
   1.503 +
   1.504 +	return true;
   1.505 +}
   1.506 +
   1.507 +bool DoLineNumberTokeniser()
   1.508 +{
   1.509 +	while(!ErrorNum && !EndOfFile)
   1.510 +	{
   1.511 +		if(NumberStart)
   1.512 +		{
   1.513 +			// tokenise line number
   1.514 +			Uint16 LineNumber = NumberValue ^ 0x4040;
   1.515 +
   1.516 +			WriteByte(0x8d);
   1.517 +
   1.518 +			WriteByte(((LineNumber&0xc0) >> 2) | ((LineNumber&0xc000) >> 12) | 0x40);
   1.519 +			WriteByte((LineNumber&0x3f) | 0x40);
   1.520 +			WriteByte(((LineNumber >> 8)&0x3f) | 0x40);
   1.521 +
   1.522 +			EatCharacters(NumberLength);
   1.523 +		}
   1.524 +		else
   1.525 +			switch(Token)
   1.526 +			{
   1.527 +				// whitespace and commas do not cause this mode to exit
   1.528 +				case ' ':
   1.529 +				case ',':
   1.530 +					WriteByte(Token);
   1.531 +					EatCharacters(1);
   1.532 +				break;
   1.533 +
   1.534 +				// hex numbers get through unscathed too
   1.535 +				case '&':
   1.536 +					WriteByte(Token);
   1.537 +					EatCharacters(1);
   1.538 +
   1.539 +					while(
   1.540 +						!ErrorNum &&
   1.541 +						!EndOfFile &&
   1.542 +						(
   1.543 +							(IncomingBuffer[0] >= '0' && IncomingBuffer[0] <= '9') ||
   1.544 +							(IncomingBuffer[0] >= 'A' && IncomingBuffer[0] <= 'F')
   1.545 +						)
   1.546 +					)
   1.547 +					{
   1.548 +						WriteByte(IncomingBuffer[0]);
   1.549 +						EatCharacters(1);
   1.550 +					}
   1.551 +				break;
   1.552 +
   1.553 +				/* grab strings without tokenising numbers */
   1.554 +				case '"':
   1.555 +					if(!CopyStringLiteral())
   1.556 +						return false;
   1.557 +				break;
   1.558 +
   1.559 +				/* default action is to turn off line number tokenising and get back to normal */
   1.560 +				default: return true;
   1.561 +			}
   1.562 +	}
   1.563 +	return true;
   1.564 +}
   1.565 +
   1.566 +bool EncodeLine()
   1.567 +{
   1.568 +	bool StartOfStatement = true;
   1.569 +
   1.570 +	/* continue until we hit a '\n' or file ends */
   1.571 +	while(!EndOfFile && Token != '\n' && !ErrorNum)
   1.572 +	{
   1.573 +		/* even if this looks like a keyword, it really isn't if the conditional flag is set & the next char is alphanumeric*/
   1.574 +		if(
   1.575 +			Token >= 0x80 &&
   1.576 +			(KeyWordTable[Token - 0x80].Flags&1) &&
   1.577 +			AlphaNumeric(NextChar)
   1.578 +			)
   1.579 +			Token = IncomingBuffer[0];
   1.580 +
   1.581 +		if(Token < 0x80)	//if not a keyword token
   1.582 +		{
   1.583 +			switch(Token)
   1.584 +			{
   1.585 +				default:	//default is dump character to memory
   1.586 +					WriteByte(Token);
   1.587 +
   1.588 +					if(Token == ':') // a colon always switches the tokeniser back to "start of statement" mode
   1.589 +						StartOfStatement = true;
   1.590 +
   1.591 +					// grab entire variables rather than allowing bits to be tokenised
   1.592 +					if
   1.593 +					(
   1.594 +						(Token >= 'a' && Token <= 'z') ||
   1.595 +						(Token >= 'A' && Token <= 'Z')
   1.596 +					)
   1.597 +					{
   1.598 +						StartOfStatement = false;
   1.599 +						EatCharacters(1);
   1.600 +						while(AlphaNumeric(IncomingBuffer[0]))
   1.601 +						{
   1.602 +							WriteByte(IncomingBuffer[0]);
   1.603 +							EatCharacters(1);
   1.604 +						}
   1.605 +					}
   1.606 +					else
   1.607 +						EatCharacters(1);
   1.608 +
   1.609 +				break;
   1.610 +				case '*':
   1.611 +					WriteByte(Token);
   1.612 +					EatCharacters(1);
   1.613 +
   1.614 +					if(StartOfStatement)
   1.615 +					{
   1.616 +						/* * at start of statement means don't tokenise rest of statement, other than string literals */
   1.617 +						while(!EndOfFile && !ErrorNum && IncomingBuffer[0] != ':' && IncomingBuffer[0] != '\n')
   1.618 +						{
   1.619 +							switch(IncomingBuffer[0])
   1.620 +							{
   1.621 +								default:
   1.622 +									WriteByte(IncomingBuffer[0]);
   1.623 +									EatCharacters(1);
   1.624 +								break;
   1.625 +								case '"':
   1.626 +									if(!CopyStringLiteral())
   1.627 +										return false;
   1.628 +								break;
   1.629 +							}
   1.630 +						}
   1.631 +					}
   1.632 +				break;
   1.633 +				case '"':
   1.634 +					if(!CopyStringLiteral())
   1.635 +						return false;
   1.636 +				break;
   1.637 +			}
   1.638 +		}
   1.639 +		else
   1.640 +		{
   1.641 +			Uint8 Flags = KeyWordTable[Token - 0x80].Flags; //make copy of flags, as we're about to throwaway Token
   1.642 +
   1.643 +			WriteByte(Token);	//write token
   1.644 +			EatCharacters(KeyWordTable[Token - 0x80].StrLen);
   1.645 +
   1.646 +			/*
   1.647 +			
   1.648 +				Effect token flags
   1.649 +			
   1.650 +			*/
   1.651 +			if(Flags & 0x08)
   1.652 +			{
   1.653 +				/* FN or PROC, so duplicate next set of alphanumerics without thought */
   1.654 +				while(!ErrorNum && !EndOfFile && AlphaNumeric(IncomingBuffer[0]))
   1.655 +				{
   1.656 +					WriteByte(IncomingBuffer[0]);
   1.657 +					EatCharacters(1);
   1.658 +				}
   1.659 +			}
   1.660 +
   1.661 +			if(Flags & 0x10)
   1.662 +			{
   1.663 +				/* tokenise line numbers for a bit */
   1.664 +				if(!DoLineNumberTokeniser())
   1.665 +					return false;
   1.666 +			}
   1.667 +
   1.668 +			if(Flags & 0x20)
   1.669 +			{
   1.670 +				/* REM or DATA, so copy rest of line without tokenisation */
   1.671 +				while(!ErrorNum && !EndOfFile && IncomingBuffer[0] != '\n')
   1.672 +				{
   1.673 +					WriteByte(IncomingBuffer[0]);
   1.674 +					EatCharacters(1);
   1.675 +				}
   1.676 +			}
   1.677 +
   1.678 +			if(
   1.679 +				(Flags & 0x40) && StartOfStatement
   1.680 +			)
   1.681 +			{
   1.682 +				/* pseudo-variable flag */
   1.683 +				Memory[Addr-1] += 0x40;	//adjust just-written token
   1.684 +			}
   1.685 +
   1.686 +			/* check if we now go into middle of statement */
   1.687 +			if(Flags & 0x02)
   1.688 +				StartOfStatement = false;
   1.689 +
   1.690 +			/* check if we now go into start of statement */
   1.691 +			if(Flags & 0x04)
   1.692 +				StartOfStatement = true;
   1.693 +		}
   1.694 +	}
   1.695 +
   1.696 +	EatCharacters(1);	//either eat a '\n' or have no effect at all
   1.697 +
   1.698 +	return true;
   1.699 +}
   1.700 +
   1.701 +bool ImportBASIC(const char *Filename, Uint8 *Mem, int* Size)
   1.702 +{
   1.703 +	/* store memory target to global var */
   1.704 +	Memory = Mem;
   1.705 +	ErrorNum = 0;
   1.706 +	Addr = 0;
   1.707 +
   1.708 +#if 0
   1.709 +	/* get the value of PAGE‚ insert BASIC code starting from there */
   1.710 +	Addr = Memory[0x18] << 8;
   1.711 +
   1.712 +	/* validity check: does PAGE currently point to a 0x0d? */
   1.713 +	if(Memory[Addr] != 0x0d)
   1.714 +	{
   1.715 +		ErrorNum = 1;
   1.716 +		return false;
   1.717 +	}
   1.718 +
   1.719 +	/* validity check: does TOP - 2 point to a 0x0d, 0xff? */
   1.720 +	Uint16 TOPAddr = Memory[0x12] | (Memory[0x13] << 8);
   1.721 +	if(
   1.722 +		(Memory[TOPAddr-2] != 0x0d) ||
   1.723 +		(Memory[TOPAddr-1] != 0xff)
   1.724 +	)
   1.725 +	{
   1.726 +		ErrorNum = 1;
   1.727 +		return false;
   1.728 +	}
   1.729 +#endif
   1.730 +
   1.731 +	/* open file, reset variables */
   1.732 +	inputfile = fopen(Filename, "rt");
   1.733 +	IncomingPointer = 0;
   1.734 +	CurLine = 1;
   1.735 +	EndOfFile = false;
   1.736 +
   1.737 +	if(!inputfile)
   1.738 +	{
   1.739 +		ErrorNum = 2;
   1.740 +		return false;
   1.741 +	}
   1.742 +
   1.743 +	/* fill input buffer */
   1.744 +	int c = 8;
   1.745 +	while(c--)
   1.746 +		GetCharacter();
   1.747 +
   1.748 +	while(!EndOfFile && !ErrorNum)
   1.749 +	{
   1.750 +		/* get line number */
   1.751 +			/* skip white space and empty lines */
   1.752 +			while(Token == ' ' || Token == '\t' || Token == '\r' || Token == '\n')
   1.753 +				EatCharacters(1);
   1.754 +				
   1.755 +			/* end of file? */
   1.756 +			if(EndOfFile) break;
   1.757 +
   1.758 +			/* now we should see a line number */
   1.759 +			if(!NumberStart || (NumberValue >= 32768))
   1.760 +			{
   1.761 +				ErrorNum = -1;
   1.762 +				sprintf(DynamicErrorText, "Malformed line number at line %d", CurLine);
   1.763 +				break;
   1.764 +			}
   1.765 +
   1.766 +			/* inject into memory */
   1.767 +			WriteByte(0x0d);
   1.768 +			WriteByte(NumberValue >> 8);
   1.769 +			WriteByte(NumberValue&0xff);
   1.770 +			EatCharacters(NumberLength);
   1.771 +
   1.772 +		/* read rest of line, record length */
   1.773 +		Uint16 LengthAddr = Addr; WriteByte(0);
   1.774 +		if(!EncodeLine())
   1.775 +			break;
   1.776 +		Memory[LengthAddr] = static_cast<Uint8>(Addr - LengthAddr + 3);
   1.777 +	}
   1.778 +
   1.779 +	/* write "end of program" */
   1.780 +	WriteByte(0x0d);
   1.781 +	WriteByte(0xff);
   1.782 +	
   1.783 +#if 0
   1.784 +	/* write TOP */
   1.785 +	Memory[0x12] = Addr&0xff;
   1.786 +	Memory[0x13] = Addr >> 8;
   1.787 +#endif
   1.788 +
   1.789 +	// Return size of tokenised code
   1.790 +	if (Size != NULL)
   1.791 +	{
   1.792 +		*Size = Addr;
   1.793 +	}
   1.794 +
   1.795 +	fclose(inputfile);
   1.796 +	return ErrorNum ? false : true;
   1.797 +}
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/src/BASIC.h	Tue Mar 01 20:05:48 2011 +0100
     2.3 @@ -0,0 +1,39 @@
     2.4 +/*************************************************************************************************/
     2.5 +/**
     2.6 +	BASIC.h
     2.7 +
     2.8 +	Contains routines for tokenising/detokenising BBC BASIC programs.
     2.9 +
    2.10 +	Modified from code by Thomas Harte.
    2.11 +
    2.12 +	Copyright (C) Thomas Harte
    2.13 +
    2.14 +	This file is part of BeebAsm.
    2.15 +
    2.16 +	BeebAsm is free software: you can redistribute it and/or modify it under the terms of the GNU
    2.17 +	General Public License as published by the Free Software Foundation, either version 3 of the
    2.18 +	License, or (at your option) any later version.
    2.19 +
    2.20 +	BeebAsm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
    2.21 +	even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    2.22 +	GNU General Public License for more details.
    2.23 +
    2.24 +	You should have received a copy of the GNU General Public License along with BeebAsm, as
    2.25 +	COPYING.txt.  If not, see <http://www.gnu.org/licenses/>.
    2.26 +*/
    2.27 +/*************************************************************************************************/
    2.28 +
    2.29 +#ifndef BASIC_H_
    2.30 +#define BASIC_H_
    2.31 +
    2.32 +typedef unsigned char Uint8;
    2.33 +typedef unsigned short Uint16;
    2.34 +
    2.35 +void SetupBASICTables();
    2.36 +const char *GetBASICError();
    2.37 +int GetBASICErrorNum();
    2.38 +bool ExportBASIC(const char *Filename, Uint8 *Memory);
    2.39 +bool ImportBASIC(const char *Filename, Uint8 *Mem, int* Size);
    2.40 +
    2.41 +
    2.42 +#endif // BASIC_H_