mirror of
https://github.com/fadden/ciderpress.git
synced 2024-11-26 02:49:20 +00:00
8ecb8e35eb
Should be "INSTR(" rather than "INSTR$(". Fixes issue #38.
832 lines
31 KiB
C++
832 lines
31 KiB
C++
/*
|
|
* CiderPress
|
|
* Copyright (C) 2007, 2008 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]
|
|
* <line> ...
|
|
* <EOF marker ($0000)>
|
|
*
|
|
* Each line consists of:
|
|
* <16-bit address of next line (relative to $800)>
|
|
* <16-bit line number, usually 0-63999>
|
|
* <tokens | characters> ...
|
|
* <EOL marker ($00)>
|
|
*
|
|
* 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 * ReformatApplesoft::kTokenLen] = {
|
|
"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 uint8_t* 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) {
|
|
LOGI(" BAS truncated?");
|
|
//fExpBuf.CreateWorkBuf();
|
|
BufPrintf("\r\n");
|
|
goto done;
|
|
}
|
|
|
|
while (length > 0) {
|
|
uint16_t nextAddr;
|
|
uint16_t 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) {
|
|
LOGI(" BAS ended early; len is %d", length);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* print line number */
|
|
RTFSetColor(kLineNumColor);
|
|
lineNum = Read16(&srcPtr, &length);
|
|
BufPrintf(" %u ", lineNum);
|
|
|
|
RTFSetColor(kDefaultColor);
|
|
|
|
assert(kTokenLen == 8); // we do "<< 3" below, so this must hold
|
|
|
|
/* 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 character */
|
|
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 if (inRem && *srcPtr == '\r') {
|
|
RTFNewPara();
|
|
} else {
|
|
RTFPrintChar(*srcPtr);
|
|
}
|
|
} else {
|
|
if (inRem && *srcPtr == '\r') {
|
|
BufPrintf("\r\n");
|
|
} else {
|
|
BufPrintf("%c", *srcPtr);
|
|
}
|
|
}
|
|
}
|
|
|
|
srcPtr++;
|
|
length--;
|
|
}
|
|
|
|
if (inQuote || inRem)
|
|
RTFSetColor(kDefaultColor);
|
|
inQuote = inRem = false;
|
|
|
|
srcPtr++;
|
|
length--;
|
|
|
|
if (!length) {
|
|
LOGI(" BAS truncated in mid-line");
|
|
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]
|
|
* <line> ...
|
|
*
|
|
* Each line consists of:
|
|
* <8-bit line length>
|
|
* <16-bit line number>
|
|
* <token | character | variable> ...
|
|
* <end-of-line token ($01)>
|
|
*
|
|
* 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:", "<EOL>", "_ ", ":",
|
|
"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 uint8_t* srcPtr = pHolder->GetSourceBuf(part);
|
|
long srcLen = pHolder->GetSourceLen(part);
|
|
long length = srcLen;
|
|
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) {
|
|
LOGI(" INT truncated?");
|
|
BufPrintf("\r\n");
|
|
goto done;
|
|
}
|
|
|
|
while (length > 0) {
|
|
uint8_t lineLen;
|
|
uint16_t lineNum;
|
|
bool trailingSpace;
|
|
bool newTrailingSpace = false;
|
|
|
|
/* pull the length byte, which we sanity-check */
|
|
lineLen = *srcPtr++;
|
|
length--;
|
|
if (lineLen == 0) {
|
|
LOGI(" INT found zero-length line?");
|
|
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) {
|
|
LOGI(" INT ended while in a string constant");
|
|
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) {
|
|
LOGI(" INT ended while in a REM statement");
|
|
break;
|
|
}
|
|
} else if (*srcPtr >= 0xb0 && *srcPtr <= 0xb9) {
|
|
/* start of integer constant */
|
|
srcPtr++;
|
|
length--;
|
|
if (length < 2) {
|
|
LOGI(" INT ended while in an integer constant");
|
|
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 */
|
|
LOGI(" INT unexpected value 0x%02x at byte %d",
|
|
*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) {
|
|
LOGI("bailing"); // must've failed during processing
|
|
goto bail;
|
|
}
|
|
srcPtr++;
|
|
length--;
|
|
|
|
RTFNewPara();
|
|
}
|
|
|
|
done:
|
|
RTFEnd();
|
|
|
|
SetResultBuffer(pOutput);
|
|
retval = 0;
|
|
|
|
bail:
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* ===========================================================================
|
|
* Apple /// Business BASIC
|
|
* ===========================================================================
|
|
*/
|
|
|
|
/*
|
|
* Apple /// Business BASIC file format:
|
|
*
|
|
* <16-bit file length>
|
|
* <line> ...
|
|
* <EOF marker ($0000)>
|
|
*
|
|
* Each line consists of:
|
|
* <8-bit offset to next line>
|
|
* <16-bit line number, usually 0-63999>
|
|
* <tokens | characters> ...
|
|
* <EOL marker ($00)>
|
|
*
|
|
* All values are little-endian. Numbers are stored as characters.
|
|
*/
|
|
|
|
/*
|
|
* Decide whether or not we want to handle this file.
|
|
*/
|
|
void ReformatBusiness::Examine(ReformatHolder* pHolder)
|
|
{
|
|
ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot;
|
|
|
|
if (pHolder->GetFileType() == kTypeBA3)
|
|
applies = ReformatHolder::kApplicYes;
|
|
|
|
pHolder->SetApplic(ReformatHolder::kReformatBusiness, applies,
|
|
ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
|
|
pHolder->SetApplic(ReformatHolder::kReformatBusiness_Hilite, applies,
|
|
ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
|
|
|
|
if (pHolder->GetOption(ReformatHolder::kOptHiliteBASIC) != 0)
|
|
pHolder->SetApplicPreferred(ReformatHolder::kReformatBusiness_Hilite);
|
|
else
|
|
pHolder->SetApplicPreferred(ReformatHolder::kReformatBusiness);
|
|
}
|
|
|
|
/*
|
|
* Values from 128 to 234 are tokens in Business BASIC. 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.
|
|
* TODO: verify this comment -- looks like copy & paste from BAS token table
|
|
*/
|
|
static const char gBusinessTokens[128*10] = {
|
|
/* 0x80 */ "END\0 FOR\0 NEXT\0 INPUT\0 OUTPUT\0 DIM\0 READ\0 WRITE\0 "
|
|
/* 0x88 */ "OPEN\0 CLOSE\0 *error*\0 TEXT\0 *error*\0 BYE\0 *error*\0 *error*\0 "
|
|
/* 0x90 */ "*error*\0 *error*\0 *error*\0 WINDOW\0 INVOKE\0 PERFORM\0 *error*\0 *error*\0 "
|
|
/* 0x98 */ "FRE\0 HPOS\0 VPOS\0 ERRLIN\0 ERR\0 KBD\0 EOF\0 TIME$\0 "
|
|
/* 0xa0 */ "DATE$\0 PREFIX$\0 EXFN.\0 EXFN%.\0 OUTREC\0 INDENT\0 *error*\0 *error*\0 "
|
|
/* 0xa8 */ "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 POP\0 HOME\0 *error*\0 "
|
|
/* 0xb0 */ "SUB$(\0 OFF\0 TRACE\0 NOTRACE\0 NORMAL\0 INVERSE\0 SCALE(\0 RESUME\0 "
|
|
/* 0xb8 */ "*error*\0 LET\0 GOTO\0 IF\0 RESTORE\0 SWAP\0 GOSUB\0 RETURN\0 "
|
|
/* 0xc0 */ "REM\0 STOP\0 ON\0 *error*\0 LOAD\0 SAVE\0 DELETE\0 RUN\0 "
|
|
/* 0xc8 */ "RENAME\0 LOCK\0 UNLOCK\0 CREATE\0 EXEC\0 CHAIN\0 *error*\0 *error*\0 "
|
|
/* 0xd0 */ "*error*\0 CATALOG\0 *error*\0 *error*\0 DATA\0 IMAGE\0 CAT\0 DEF\0 "
|
|
/* 0xd8 */ "*error*\0 PRINT\0 DEL\0 ELSE\0 CONT\0 LIST\0 CLEAR\0 GET\0 "
|
|
/* 0xe0 */ "NEW\0 TAB\0 TO\0 SPC(\0 USING\0 THEN\0 *error*\0 MOD\0 "
|
|
/* 0xe8 */ "STEP\0 AND\0 OR\0 EXTENSION\0DIV\0 *error*\0 FN\0 NOT\0 "
|
|
/* 0xf0 */ "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 "
|
|
/* 0xf8 */ "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 tf7\0 "
|
|
};
|
|
|
|
static const char gExtendedBusinessTokens[128*10] = {
|
|
/* 0x80 */ "TAB(\0 TO\0 SPC(\0 USING\0 THEN\0 *error*\0 MOD\0 STEP\0 "
|
|
/* 0x88 */ "AND\0 OR\0 EXTENSION\0DIV\0 *error*\0 FN\0 NOT\0 *error*\0 "
|
|
/* 0x90 */ "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 "
|
|
/* 0x98 */ "*error*\0 *error*\0 *error*\0 *error*\0 AS\0 SGN(\0 INT(\0 ABS(\0 "
|
|
/* 0xa0 */ "*error*\0 TYP(\0 REC(\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 "
|
|
/* 0xa8 */ "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 PDL(\0 BUTTON(\0 SQR(\0 "
|
|
/* 0xb0 */ "RND(\0 LOG(\0 EXP(\0 COS(\0 SIN(\0 TAN(\0 ATN(\0 *error*\0 "
|
|
/* 0xb8 */ "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 "
|
|
/* 0xc0 */ "*error*\0 *error*\0 *error*\0 STR$(\0 HEX$(\0 CHR$(\0 LEN(\0 VAL(\0 "
|
|
/* 0xc8 */ "ASC(\0 TEN(\0 *error*\0 *error*\0 CONV(\0 CONV&(\0 CONV$(\0 CONV%(\0 "
|
|
/* 0xd0 */ "LEFT$(\0 RIGHT$(\0 MID$(\0 INSTR(\0 *error*\0 *error*\0 *error*\0 *error*\0 "
|
|
/* 0xd8 */ "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 "
|
|
/* 0xe0 */ "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 "
|
|
/* 0xe8 */ "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 "
|
|
/* 0xf0 */ "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 "
|
|
/* 0xf8 */ "*error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 *error*\0 "
|
|
};
|
|
|
|
/*
|
|
* Reformat an Apple /// Business BASIC program into a text format that
|
|
* mimics the output of the "LIST" command.
|
|
*/
|
|
int ReformatBusiness::Process(const ReformatHolder* pHolder,
|
|
ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
|
|
ReformatOutput* pOutput)
|
|
{
|
|
const uint8_t* srcPtr = pHolder->GetSourceBuf(part);
|
|
long srcLen = pHolder->GetSourceLen(part);
|
|
long length = srcLen;
|
|
int retval = -1;
|
|
int nestLevels = 0;
|
|
|
|
if (srcLen > 65536)
|
|
fUseRTF = false;
|
|
if (fUseRTF) {
|
|
if (id != ReformatHolder::kReformatBusiness_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-BASIC file.
|
|
*/
|
|
if (length < 2) {
|
|
LOGI(" BA3 truncated?");
|
|
//fExpBuf.CreateWorkBuf();
|
|
BufPrintf("\r\n");
|
|
goto done;
|
|
}
|
|
|
|
uint16_t fileLength;
|
|
fileLength = Read16(&srcPtr, &length);
|
|
LOGI(" BA3 internal file length is: %d", fileLength);
|
|
|
|
while (length > 0) {
|
|
uint16_t increment;
|
|
uint16_t extendedToken;
|
|
uint16_t lineNum;
|
|
bool inQuote = false;
|
|
bool inRem = false;
|
|
bool firstData = true;
|
|
bool literalYet = false;
|
|
|
|
increment = Read8(&srcPtr, &length);
|
|
LOGI(" BA3 increment to next line is: %d", increment);
|
|
if (increment == 0) {
|
|
/* ProDOS sticks an extra byte on the end? */
|
|
if (length > 1) {
|
|
LOGI(" BA3 ended early; len is %d", length);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* print line number */
|
|
RTFSetColor(kLineNumColor);
|
|
lineNum = Read16(&srcPtr, &length);
|
|
LOGI(" BA3 line number: %d", lineNum);
|
|
BufPrintf(" %u ", lineNum);
|
|
|
|
RTFSetColor(kDefaultColor);
|
|
if (nestLevels > 0) {
|
|
for (int i =0; i < nestLevels; i++)
|
|
BufPrintf(" ");
|
|
}
|
|
|
|
/* print a line */
|
|
while (*srcPtr != 0 && length > 0) {
|
|
if (*srcPtr & 0x80) {
|
|
/* token */
|
|
//RTFBoldOn();
|
|
literalYet = false;
|
|
RTFSetColor(kKeywordColor);
|
|
if (*srcPtr == 0x81) {
|
|
// Token is FOR - indent
|
|
nestLevels ++;
|
|
}
|
|
else if (*srcPtr == 0x82) {
|
|
// Token is NEXT - outdent
|
|
nestLevels --;
|
|
}
|
|
if (!firstData)
|
|
BufPrintf(" ");
|
|
if (((*srcPtr) & 0x7f) == 0x7f)
|
|
{
|
|
extendedToken = Read8(&srcPtr, &length);
|
|
BufPrintf("%s", &gExtendedBusinessTokens[((*srcPtr) & 0x7f) * 10]);
|
|
// We need to have some tokens NOT add a space after them.
|
|
if ((*srcPtr == 0x80) || // TAB(
|
|
(*srcPtr == 0x82) || // SPC(
|
|
(*srcPtr == 0x9d) || // SGN(
|
|
(*srcPtr == 0x9e) || // INT(
|
|
(*srcPtr == 0x9f) || // ABS(
|
|
(*srcPtr == 0xa1) || // TYP(
|
|
(*srcPtr == 0xa2) || // REC(
|
|
(*srcPtr == 0xad) || // PDL(
|
|
(*srcPtr == 0xae) || // BUTTON(
|
|
(*srcPtr == 0xaf) || // SQR(
|
|
(*srcPtr == 0xb0) || // RND(
|
|
(*srcPtr == 0xb1) || // LOG(
|
|
(*srcPtr == 0xb2) || // EXP(
|
|
(*srcPtr == 0xb3) || // COS(
|
|
(*srcPtr == 0xb4) || // SIN(
|
|
(*srcPtr == 0xb5) || // TAN(
|
|
(*srcPtr == 0xb6) || // ATN(
|
|
(*srcPtr == 0xc3) || // STR$(
|
|
(*srcPtr == 0xc4) || // HEX$(
|
|
(*srcPtr == 0xc5) || // CHR$(
|
|
(*srcPtr == 0xc6) || // LEN(
|
|
(*srcPtr == 0xc7) || // VAL(
|
|
(*srcPtr == 0xc8) || // ASC(
|
|
(*srcPtr == 0xc9) || // TEN(
|
|
(*srcPtr == 0xcc) || // CONV(
|
|
(*srcPtr == 0xcd) || // CONV&(
|
|
(*srcPtr == 0xce) || // CONV$(
|
|
(*srcPtr == 0xcf) || // CONV%(
|
|
(*srcPtr == 0xd0) || // LEFT$(
|
|
(*srcPtr == 0xd1) || // RIGHT$(
|
|
(*srcPtr == 0xd2) || // MID$(
|
|
(*srcPtr == 0xd3)) // INSTR(
|
|
firstData = true;
|
|
else
|
|
firstData = false;
|
|
}
|
|
else {
|
|
BufPrintf("%s", &gBusinessTokens[((*srcPtr) & 0x7f) * 10]);
|
|
// We need to have some tokens NOT add a space after them.
|
|
if ((*srcPtr == 0x99) || // HPOS
|
|
(*srcPtr == 0x9a) || // VPOS
|
|
(*srcPtr == 0x9f) || // TIME$
|
|
(*srcPtr == 0xa0) || // DATE$
|
|
(*srcPtr == 0xa1) || // PREFIX$
|
|
(*srcPtr == 0xa2) || // EXFN.
|
|
(*srcPtr == 0xa3) || // EXFN%.
|
|
(*srcPtr == 0xb0) || // SUB$(.
|
|
(*srcPtr == 0xb6) || // SCALE(
|
|
(*srcPtr == 0xc0) || // REM
|
|
(*srcPtr == 0xe3)) // SPC(
|
|
firstData = true;
|
|
else
|
|
firstData = false;
|
|
}
|
|
//RTFBoldOff();
|
|
RTFSetColor(kDefaultColor);
|
|
|
|
if (*srcPtr == 0xc0) {
|
|
// REM -- do rest of line in green
|
|
RTFSetColor(kCommentColor);
|
|
inRem = true;
|
|
}
|
|
} else {
|
|
/* simple chracter */
|
|
if (*srcPtr == ':') // Reset line if we have a colon
|
|
firstData = true;
|
|
if (!firstData) {
|
|
if (!literalYet) {
|
|
BufPrintf(" ");
|
|
literalYet = true;
|
|
}
|
|
}
|
|
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) {
|
|
LOGI(" BA3 truncated in mid-line");
|
|
break;
|
|
}
|
|
|
|
RTFNewPara();
|
|
}
|
|
|
|
done:
|
|
RTFEnd();
|
|
|
|
SetResultBuffer(pOutput);
|
|
retval = 0;
|
|
|
|
//bail:
|
|
return retval;
|
|
}
|