/* * CiderPress * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. * See the file LICENSE for distribution terms. */ /* * Convert BASIC programs. */ #include "StdAfx.h" #include "BASIC.h" /* our color map */ const ReformatText::TextColor kDefaultColor = ReformatText::kColorDarkGrey; const ReformatText::TextColor kLineNumColor = ReformatText::kColorDarkGrey; const ReformatText::TextColor kKeywordColor = ReformatText::kColorBlack; const ReformatText::TextColor kCommentColor = ReformatText::kColorMediumGreen; const ReformatText::TextColor kStringColor = ReformatText::kColorMediumBlue; const ReformatText::TextColor kColonColor = ReformatText::kColorRed; //kColorMediumGrey; /* * =========================================================================== * Applesoft BASIC * =========================================================================== */ /* * Applesoft BASIC file format: * * <16-bit file length> [DOS 3.3 only; not visible here] * ... * * * Each line consists of: * <16-bit address of next line (relative to $800)> * <16-bit line number, usually 0-63999> * ... * * * All values are little-endian. Numbers are stored as characters. */ /* * Decide whether or not we want to handle this file. */ void ReformatApplesoft::Examine(ReformatHolder* pHolder) { ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot; if (pHolder->GetFileType() == kTypeBAS) applies = ReformatHolder::kApplicProbably; pHolder->SetApplic(ReformatHolder::kReformatApplesoft, applies, ReformatHolder::kApplicNot, ReformatHolder::kApplicNot); pHolder->SetApplic(ReformatHolder::kReformatApplesoft_Hilite, applies, ReformatHolder::kApplicNot, ReformatHolder::kApplicNot); if (pHolder->GetOption(ReformatHolder::kOptHiliteBASIC) != 0) pHolder->SetApplicPreferred(ReformatHolder::kReformatApplesoft_Hilite); else pHolder->SetApplicPreferred(ReformatHolder::kReformatApplesoft); } /* * Values from 128 to 234 are tokens in Applesoft. Values from 235 to 255 * show up as error messages. The goal here is to produce values that are * human-readable and/or EXECable, so no attempt has been made to display * the error values. */ static const char gApplesoftTokens[128*8] = { "END\0 FOR\0 NEXT\0 DATA\0 INPUT\0 DEL\0 DIM\0 READ\0 " "GR\0 TEXT\0 PR#\0 IN#\0 CALL\0 PLOT\0 HLIN\0 VLIN\0 " "HGR2\0 HGR\0 HCOLOR=\0HPLOT\0 DRAW\0 XDRAW\0 HTAB\0 HOME\0 " "ROT=\0 SCALE=\0 SHLOAD\0 TRACE\0 NOTRACE\0NORMAL\0 INVERSE\0FLASH\0 " "COLOR=\0 POP\0 VTAB\0 HIMEM:\0 LOMEM:\0 ONERR\0 RESUME\0 RECALL\0 " "STORE\0 SPEED=\0 LET\0 GOTO\0 RUN\0 IF\0 RESTORE\0&\0 " "GOSUB\0 RETURN\0 REM\0 STOP\0 ON\0 WAIT\0 LOAD\0 SAVE\0 " "DEF\0 POKE\0 PRINT\0 CONT\0 LIST\0 CLEAR\0 GET\0 NEW\0 " "TAB(\0 TO\0 FN\0 SPC(\0 THEN\0 AT\0 NOT\0 STEP\0 " "+\0 -\0 *\0 /\0 ^\0 AND\0 OR\0 >\0 " "=\0 <\0 SGN\0 INT\0 ABS\0 USR\0 FRE\0 SCRN(\0 " "PDL\0 POS\0 SQR\0 RND\0 LOG\0 EXP\0 COS\0 SIN\0 " "TAN\0 ATN\0 PEEK\0 LEN\0 STR$\0 VAL\0 ASC\0 CHR$\0 " "LEFT$\0 RIGHT$\0 MID$\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 " "ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 " "ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 ERROR\0 " }; /* * Make table available. */ /*static*/ const char* ReformatApplesoft::GetApplesoftTokens(void) { return gApplesoftTokens; } /* * Reformat an Applesoft BASIC program into a text format that mimics the * output of the "LIST" command (with POKE 33,73 to suppress CRs). */ int ReformatApplesoft::Process(const ReformatHolder* pHolder, ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part, ReformatOutput* pOutput) { const unsigned char* srcPtr = pHolder->GetSourceBuf(part); long srcLen = pHolder->GetSourceLen(part); long length = srcLen; int retval = -1; if (srcLen > 65536) fUseRTF = false; if (fUseRTF) { if (id != ReformatHolder::kReformatApplesoft_Hilite) fUseRTF = false; } RTFBegin(kRTFFlagColorTable); /* * Make sure there's enough here to get started. We want to return an * "okay" result because we want this treated like a reformatted empty * BASIC program rather than a non-Applesoft file. */ if (length < 2) { WMSG0(" BAS truncated?\n"); //fExpBuf.CreateWorkBuf(); BufPrintf("\r\n"); goto done; } while (length > 0) { unsigned short nextAddr; unsigned short lineNum; bool inQuote = false; bool inRem = false; nextAddr = Read16(&srcPtr, &length); if (nextAddr == 0) { /* ProDOS sticks an extra byte on the end? */ if (length > 1) { WMSG1(" BAS ended early; len is %d\n", length); } break; } /* print line number */ RTFSetColor(kLineNumColor); lineNum = Read16(&srcPtr, &length); BufPrintf(" %u ", lineNum); RTFSetColor(kDefaultColor); /* print a line */ while (*srcPtr != 0 && length > 0) { if (*srcPtr & 0x80) { /* token */ //RTFBoldOn(); RTFSetColor(kKeywordColor); BufPrintf(" %s ", &gApplesoftTokens[((*srcPtr) & 0x7f) << 3]); //RTFBoldOff(); RTFSetColor(kDefaultColor); if (*srcPtr == 0xb2) { // REM -- do rest of line in green RTFSetColor(kCommentColor); inRem = true; } } else { /* simple chracter */ if (fUseRTF) { if (*srcPtr == '"' && !inRem) { if (!inQuote) { RTFSetColor(kStringColor); RTFPrintChar(*srcPtr); } else { RTFPrintChar(*srcPtr); RTFSetColor(kDefaultColor); } inQuote = !inQuote; } else if (*srcPtr == ':' && !inRem && !inQuote) { RTFSetColor(kColonColor); RTFPrintChar(*srcPtr); RTFSetColor(kDefaultColor); } else { RTFPrintChar(*srcPtr); } } else { BufPrintf("%c", *srcPtr); } } srcPtr++; length--; } if (inQuote || inRem) RTFSetColor(kDefaultColor); inQuote = inRem = false; srcPtr++; length--; if (!length) { WMSG0(" BAS truncated in mid-line\n"); break; } RTFNewPara(); } done: RTFEnd(); SetResultBuffer(pOutput); retval = 0; //bail: return retval; } /* * =========================================================================== * Integer BASIC * =========================================================================== */ #include "Asm.h" /* * Integer BASIC file format (thanks to Paul Schlyter, pausch at saaf.se): * * <16-bit file length> [DOS 3.3 only; not visible here] * ... * * Each line consists of: * <8-bit line length> * <16-bit line number> * ... * * * Each line is a stream of bytes: * $01: end of line * $12-$7f: language token * $b0-b9 ('0'-'9'): start of integer constant (first byte has no meaning?) * next 16 bits hold the number * $c1-da ('A'-'Z'): start of a variable name; ends on value <0x80 * next several bytes hold the name * * Most of the first $11 tokens are illegal except as part of an integer * constant, which just means that you can't type them into a program from * the keyboard. If you POKE them in manually, things like "himem:" and * "del" will work. * * $ba-$c0 and $db-$ff are illegal except in a string constant. * * There is no end-of-file marker. */ /* * Tokens. */ static const char* const gIntegerTokens[128] = { "HIMEM:", "", "_ ", ":", "LOAD ", "SAVE ", "CON ", "RUN ", "RUN ", "DEL ", ",", "NEW ", "CLR ", "AUTO ", ",", "MAN ", "HIMEM:", "LOMEM:", "+", "-", "*", "/", "=", "#", ">=", ">", "<=", "<>", "<", "AND ", "OR ", "MOD ", "^ ", "+", "(", ",", "THEN ", "THEN ", ",", ",", "\"", "\"", "(", "!", "!", "(", "PEEK ", "RND ", "SGN ", "ABS ", "PDL ", "RNDX ", "(", "+", "-", "NOT ", "(", "=", "#", "LEN(", "ASC( ", "SCRN( ", ",", "(", "$", "$", "(", ",", ",", ";", ";", ";", ",", ",", ",", "TEXT ", "GR ", "CALL ", "DIM ", "DIM ", "TAB ", "END ", "INPUT ", "INPUT ", "INPUT ", "FOR ", "=", "TO ", "STEP ", "NEXT ", ",", "RETURN ", "GOSUB ", "REM ", "LET ", "GOTO ", "IF ", "PRINT ", "PRINT ", "PRINT ", "POKE ", ",", "COLOR= ", "PLOT ", ",", "HLIN ", ",", "AT ", "VLIN ", ",", "AT ", "VTAB ", "=", "=", ")", ")", "LIST ", ",", "LIST ", "POP ", "NODSP ", "NODSP ", "NOTRACE ", "DSP ", "DSP ", "TRACE ", "PR# ", "IN# " }; /* * Decide whether or not we want to handle this file. */ void ReformatInteger::Examine(ReformatHolder* pHolder) { ReformatHolder::ReformatApplies apply = ReformatHolder::kApplicNot; if (pHolder->GetFileType() == kTypeINT) { if (ReformatSCAssem::IsSCAssem(pHolder)) { /* possibly intbasic */ apply = ReformatHolder::kApplicMaybe; } else if (ReformatLISA3::IsLISA(pHolder)) { /* possibly intbasic */ apply = ReformatHolder::kApplicMaybe; } else if (ReformatLISA4::IsLISA(pHolder)) { /* possibly intbasic */ apply = ReformatHolder::kApplicMaybe; } else { /* definitely intbasic */ apply = ReformatHolder::kApplicYes; } } else { /* not intbasic */ apply = ReformatHolder::kApplicNot; } //apply = ReformatHolder::kApplicMaybe; // DEBUG DEBUG pHolder->SetApplic(ReformatHolder::kReformatInteger, apply, ReformatHolder::kApplicNot, ReformatHolder::kApplicNot); pHolder->SetApplic(ReformatHolder::kReformatInteger_Hilite, apply, ReformatHolder::kApplicNot, ReformatHolder::kApplicNot); if (pHolder->GetOption(ReformatHolder::kOptHiliteBASIC) != 0) pHolder->SetApplicPreferred(ReformatHolder::kReformatInteger_Hilite); else pHolder->SetApplicPreferred(ReformatHolder::kReformatInteger); } /* * Reformat an Integer BASIC program into a text format that mimics the * output of the "LIST" command. */ int ReformatInteger::Process(const ReformatHolder* pHolder, ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part, ReformatOutput* pOutput) { const unsigned char* srcPtr = pHolder->GetSourceBuf(part); long srcLen = pHolder->GetSourceLen(part); long length = srcLen; //unsigned short val16; int retval = -1; //srcPtr += 0xff0; //0x228e; //srcLen -= 0xff0; //0x228e; if (srcLen > 65536) fUseRTF = false; if (fUseRTF) { if (id != ReformatHolder::kReformatInteger_Hilite) fUseRTF = false; } RTFBegin(kRTFFlagColorTable); /* * Make sure there's enough here to get started. We want to return an * "okay" result because we want this treated like a reformatted empty * BASIC program rather than a non-Integer file. */ if (length < 2) { WMSG0(" INT truncated?\n"); BufPrintf("\r\n"); goto done; } while (length > 0) { unsigned char lineLen; unsigned short lineNum; bool trailingSpace; bool newTrailingSpace = false; /* pull the length byte, which we sanity-check */ lineLen = *srcPtr++; length--; if (lineLen == 0) { WMSG0(" INT found zero-length line?\n"); break; } /* line number */ RTFSetColor(kLineNumColor); lineNum = Read16(&srcPtr, &length); BufPrintf("%5u ", lineNum); RTFSetColor(kDefaultColor); trailingSpace = true; while (*srcPtr != 0x01 && length > 0) { if (*srcPtr == 0x28) { /* start of quoted text */ RTFSetColor(kStringColor); BufPrintf("\""); length--; while (*++srcPtr != 0x29 && length > 0) { /* escape chars, but let Ctrl-D and Ctrl-G through */ if (fUseRTF && *srcPtr != 0x84 && *srcPtr != 0x87) RTFPrintChar(*srcPtr & 0x7f); else BufPrintf("%c", *srcPtr & 0x7f); length--; } if (*srcPtr != 0x29) { WMSG0(" INT ended while in a string constant\n"); break; } BufPrintf("\""); RTFSetColor(kDefaultColor); srcPtr++; length--; } else if (*srcPtr == 0x5d) { /* start of REM statement, run to EOL */ //RTFBoldOn(); RTFSetColor(kKeywordColor); BufPrintf("%sREM ", trailingSpace ? "" : " "); //RTFBoldOff(); RTFSetColor(kCommentColor); length--; while (*++srcPtr != 0x01) { if (fUseRTF) RTFPrintChar(*srcPtr & 0x7f); else BufPrintf("%c", *srcPtr & 0x7f); length--; } RTFSetColor(kDefaultColor); if (*srcPtr != 0x01) { WMSG0(" INT ended while in a REM statement\n"); break; } } else if (*srcPtr >= 0xb0 && *srcPtr <= 0xb9) { /* start of integer constant */ srcPtr++; length--; if (length < 2) { WMSG0(" INT ended while in an integer constant\n"); break; } int val; val = Read16(&srcPtr, &length); BufPrintf("%d", val); } else if (*srcPtr >= 0xc1 && *srcPtr <= 0xda) { /* start of variable name */ while ((*srcPtr >= 0xc1 && *srcPtr <= 0xda) || (*srcPtr >= 0xb0 && *srcPtr <= 0xb9)) { /* note no RTF-escaped chars in this range */ BufPrintf("%c", *srcPtr & 0x7f); srcPtr++; length--; } } else if (*srcPtr < 0x80) { /* found a token; try to get the whitespace right */ /* (maybe should've left whitespace on the ends of tokens that are always followed by whitespace...?) */ const char* token; token = gIntegerTokens[*srcPtr]; //RTFBoldOn(); if (*srcPtr == 0x03) // colon RTFSetColor(kColonColor); else RTFSetColor(kKeywordColor); if (token[0] >= 0x21 && token[0] <= 0x3f || *srcPtr < 0x12) { /* does not need leading space */ BufPrintf("%s", token); } else { /* needs leading space; combine with prev if it exists */ if (trailingSpace) BufPrintf("%s", token); else BufPrintf(" %s", token); } if (token[strlen(token)-1] == ' ') newTrailingSpace = true; //RTFBoldOff(); RTFSetColor(kDefaultColor); srcPtr++; length--; } else { /* should not happen */ WMSG2(" INT unexpected value 0x%02x at byte %d\n", *srcPtr, srcPtr - pHolder->GetSourceBuf(part)); /* skip past it and keep trying */ srcPtr++; length--; } trailingSpace = newTrailingSpace; newTrailingSpace = false; } /*while line*/ /* skip past EOL token */ if (*srcPtr != 0x01 && length > 0) { WMSG0("bailing\n"); // must've failed during processing goto bail; } srcPtr++; length--; RTFNewPara(); } done: RTFEnd(); SetResultBuffer(pOutput); retval = 0; bail: return retval; }