diff --git a/src/com/webcodepro/applecommander/compiler/ApplesoftCompiler.java b/src/com/webcodepro/applecommander/compiler/ApplesoftCompiler.java index f010ad0..844631e 100644 --- a/src/com/webcodepro/applecommander/compiler/ApplesoftCompiler.java +++ b/src/com/webcodepro/applecommander/compiler/ApplesoftCompiler.java @@ -34,6 +34,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.Stack; /** * Compile the given file as an Applesoft file. @@ -67,6 +68,10 @@ public class ApplesoftCompiler implements ApplesoftTokens { * Dynamically created map of commands to Methods. */ private Map commandMethods = new HashMap(); + /** + * Track FOR loop variables. + */ + private Stack loopVariables = new Stack(); /** * Constructor for ApplesoftCompiler. @@ -149,7 +154,7 @@ public class ApplesoftCompiler implements ApplesoftTokens { } sourceAssembly.append("LINE"); sourceAssembly.append(token.getLineNumber()); - sourceAssembly.append(":\n"); + sourceAssembly.append("\n"); do { evaluateCommand(); token = peekToken(); @@ -165,33 +170,16 @@ public class ApplesoftCompiler implements ApplesoftTokens { sourceAssembly.setLength(0); } programCode.insert(0, buildUsedAddresses()); - for (int i=0; i 0) { @@ -208,6 +196,40 @@ public class ApplesoftCompiler implements ApplesoftTokens { return buf; } + /** + * Build the variable section that is placed at the end of the + * assembly listing. + *

+ * Warning: This method re-uses the global sourceAssembly + * variables since the addAssembly method is used to format the + * code. + */ + protected StringBuffer buildVariableSection() { + sourceAssembly.setLength(0); + for (int i=0; i" + variable.getName()); - addAssembly(null, "LD" + registerLo, "#<" + variable.getName()); + addAssembly(null, "LD" + registerHi, "#>" + variable.getValue()); + addAssembly(null, "LD" + registerLo, "#<" + variable.getValue()); } else if (variable.isTypeInteger()) { addAssembly(null, "LD" + registerHi, variable.getName() + "+1"); addAssembly(null, "LD" + registerLo, variable.getName()); @@ -403,15 +446,27 @@ public class ApplesoftCompiler implements ApplesoftTokens { protected void addLoadFac(Variable variable) throws CompileException { if (variable.isConstantFloat() || variable.isTypeFloat()) { - addAssembly(null, "LDY", "#>" + variable.getName()); - addAssembly(null, "LDA", "#<" + variable.getName()); + addLoadAddress(variable, 'Y', 'A'); addAssembly(null, "JSR","MOVFM"); + } else if (variable.isConstantInteger() || variable.isTypeInteger()) { + addLoadWordValue(variable, 'A', 'Y'); + addAssembly(null, "JSR", "GIVAYF"); } else { throw new CompileException("Unable to convert to load FAC for: " + variable.getName()); } } + protected void addCopyFac(Variable variable) throws CompileException { + if (variable.isTypeFloat()) { + addLoadAddress(variable, 'Y', 'X'); + addAssembly(null, "JSR", "MOVMF"); + } else { + throw new CompileException( + "Internal Error: Can only copy floats to numeric variables."); + } + } + public void evaluateHTAB() throws CompileException { addLoadByteValue(evaluateExpression(), 'A'); addAssembly(null, "STA", "CH"); @@ -454,6 +509,88 @@ public class ApplesoftCompiler implements ApplesoftTokens { token = peekToken(); } while (token != null && !token.isEndOfCommand()); } + + public void evaluateGOTO() throws CompileException { + addAssembly(null, "JMP", getLineNumberLabel()); + } + + protected void checkSyntax(byte tokenValue, String expectedToken) throws CompileException { + ApplesoftToken token = nextToken(); + if (token.getTokenValue() != tokenValue) { + // FIXME: Should this read token from master token list? + throw new CompileException("Syntax Error: Expecting " + expectedToken); + } + } + + protected void checkSyntax(String stringValue, String expectedToken) throws CompileException { + ApplesoftToken token = nextToken(); + if (!stringValue.equals(token.getStringValue())) { + // FIXME: Should this read token from master token list? + throw new CompileException("Syntax Error: Expecting " + expectedToken); + } + } + + public void evaluateFOR() throws CompileException { + Variable loopVariable = evaluateExpression(); + if (!loopVariable.isTypeFloat()) { + throw new CompileException( + "Applesoft only allows floating-point FOR variables."); + } + checkSyntax(EQUALS, "="); + Variable startValue = evaluateNumber(); + checkSyntax(TO, "TO"); + Variable endValue = evaluateNumber(); + // FIXME: Need to handle STEP + String loopName = "FOR" + loopVariables.size(); + loopVariables.add(loopName); + addLoadFac(startValue); + addCopyFac(loopVariable); + addAssembly(loopName, null, null); + addLoadFac(endValue); + addLoadAddress(loopVariable, 'Y', 'A'); + addAssembly(null, "JSR", "FCOMP"); + addAssembly(null, "CMP", "#$FF"); // loopVariable > endValue + addAssembly(null, "BEQ", "END" + loopName); + } + + public void evaluateHPLOT() throws CompileException { + boolean firstCoordinate = true; + while (peekToken() != null && !peekToken().isEndOfCommand()) { + if (!firstCoordinate) { + checkSyntax(TO, "TO"); + } + Variable coordX = evaluateNumber(); + checkSyntax(",", ", (comma)"); + Variable coordY = evaluateNumber(); + if (firstCoordinate) { + addLoadWordValue(coordX, 'Y', 'X'); + addLoadByteValue(coordY, 'A'); + addAssembly(null, "JSR", "HPOSN"); + firstCoordinate = false; + } else { + addLoadWordValue(coordX, 'X', 'A'); + addLoadByteValue(coordY, 'Y'); + addAssembly(null, "JSR", "HLIN"); + } + } + } + + public void evaluateNEXT() throws CompileException { + // FIXME: Next ignores variable name given... + Variable variable = null; + if (!peekToken().isCommandSeparator()) { + // FIXME: This does not ensure that we only have a variable! + variable = evaluateExpression(); + } + addAssembly(null, "LDY", "#1"); + addAssembly(null, "JSR", "SNGFLT"); + addLoadAddress(variable, 'Y', 'A'); + addAssembly(null, "JSR", "FADD"); + addCopyFac(variable); + String loopName = (String) loopVariables.pop(); + addAssembly(null, "JMP", loopName); + addAssembly("END" + loopName, null, null); + } /** * Indicates if this string is a number.