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_
