mirror of
https://github.com/AppleCommander/AppleCommander.git
synced 2024-06-15 22:29:43 +00:00
Added FOR, NEXT, GOTO, HPLOT as well as fixed a few addressing
issues (related to assembly code generated).
This commit is contained in:
parent
51436c8808
commit
03d7d6f0ba
|
@ -34,6 +34,7 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compile the given file as an Applesoft file.
|
* Compile the given file as an Applesoft file.
|
||||||
|
@ -67,6 +68,10 @@ public class ApplesoftCompiler implements ApplesoftTokens {
|
||||||
* Dynamically created map of commands to Methods.
|
* Dynamically created map of commands to Methods.
|
||||||
*/
|
*/
|
||||||
private Map commandMethods = new HashMap();
|
private Map commandMethods = new HashMap();
|
||||||
|
/**
|
||||||
|
* Track FOR loop variables.
|
||||||
|
*/
|
||||||
|
private Stack loopVariables = new Stack();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for ApplesoftCompiler.
|
* Constructor for ApplesoftCompiler.
|
||||||
|
@ -149,7 +154,7 @@ public class ApplesoftCompiler implements ApplesoftTokens {
|
||||||
}
|
}
|
||||||
sourceAssembly.append("LINE");
|
sourceAssembly.append("LINE");
|
||||||
sourceAssembly.append(token.getLineNumber());
|
sourceAssembly.append(token.getLineNumber());
|
||||||
sourceAssembly.append(":\n");
|
sourceAssembly.append("\n");
|
||||||
do {
|
do {
|
||||||
evaluateCommand();
|
evaluateCommand();
|
||||||
token = peekToken();
|
token = peekToken();
|
||||||
|
@ -165,33 +170,16 @@ public class ApplesoftCompiler implements ApplesoftTokens {
|
||||||
sourceAssembly.setLength(0);
|
sourceAssembly.setLength(0);
|
||||||
}
|
}
|
||||||
programCode.insert(0, buildUsedAddresses());
|
programCode.insert(0, buildUsedAddresses());
|
||||||
for (int i=0; i<variables.size(); i++) {
|
programCode.append(buildVariableSection());
|
||||||
if (i == 0) {
|
|
||||||
sourceAssembly.append("\n");
|
|
||||||
sourceAssembly.append("* Variables:\n");
|
|
||||||
}
|
|
||||||
Variable variable = (Variable) variables.get(i);
|
|
||||||
if (variable.isConstantInteger()) {
|
|
||||||
addAssembly(variable.getName(), "DW", variable.getValue());
|
|
||||||
} else if (variable.isConstantFloat()) {
|
|
||||||
// FIXME
|
|
||||||
} else if (variable.isConstantString()) {
|
|
||||||
addAssembly(variable.getName(), "ASC", variable.getValue());
|
|
||||||
addAssembly(null, "HEX", "00");
|
|
||||||
} else if (variable.isTypeFloat()) {
|
|
||||||
addAssembly(variable.getName(), "DS", "5");
|
|
||||||
} else if (variable.isTypeInteger()) {
|
|
||||||
addAssembly(variable.getName(), "DS", "2");
|
|
||||||
} else if (variable.isTypeString()) {
|
|
||||||
// FIXME
|
|
||||||
}
|
|
||||||
}
|
|
||||||
programCode.append(sourceAssembly);
|
|
||||||
sourceLine.setLength(0);
|
sourceLine.setLength(0);
|
||||||
sourceAssembly.setLength(0);
|
sourceAssembly.setLength(0);
|
||||||
return programCode.toString().getBytes();
|
return programCode.toString().getBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a list of ROM and Zero-page addresses that are used
|
||||||
|
* within this specific program.
|
||||||
|
*/
|
||||||
protected StringBuffer buildUsedAddresses() {
|
protected StringBuffer buildUsedAddresses() {
|
||||||
StringBuffer buf = new StringBuffer();
|
StringBuffer buf = new StringBuffer();
|
||||||
if (usedAddresses.size() > 0) {
|
if (usedAddresses.size() > 0) {
|
||||||
|
@ -208,6 +196,40 @@ public class ApplesoftCompiler implements ApplesoftTokens {
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the variable section that is placed at the end of the
|
||||||
|
* assembly listing.
|
||||||
|
* <p>
|
||||||
|
* <b>Warning:</b> 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<variables.size(); i++) {
|
||||||
|
if (i == 0) {
|
||||||
|
sourceAssembly.append("\n");
|
||||||
|
sourceAssembly.append("* Variables:\n");
|
||||||
|
}
|
||||||
|
Variable variable = (Variable) variables.get(i);
|
||||||
|
if (variable.isConstantInteger()) {
|
||||||
|
addAssembly(variable.getName(), "DW", variable.getValue());
|
||||||
|
} else if (variable.isConstantFloat()) {
|
||||||
|
// FIXME
|
||||||
|
} else if (variable.isConstantString()) {
|
||||||
|
addAssembly(variable.getName(), "ASC", variable.getValue());
|
||||||
|
addAssembly(null, "HEX", "00");
|
||||||
|
} else if (variable.isTypeFloat()) {
|
||||||
|
addAssembly(variable.getName(), "HEX", "8400000000"); // = 0.0
|
||||||
|
} else if (variable.isTypeInteger()) {
|
||||||
|
addAssembly(variable.getName(), "DS", "2");
|
||||||
|
} else if (variable.isTypeString()) {
|
||||||
|
// FIXME
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sourceAssembly;
|
||||||
|
}
|
||||||
|
|
||||||
protected void evaluateCommand() {
|
protected void evaluateCommand() {
|
||||||
ApplesoftToken token = nextToken();
|
ApplesoftToken token = nextToken();
|
||||||
while (token != null && token.isCommandSeparator()) {
|
while (token != null && token.isCommandSeparator()) {
|
||||||
|
@ -265,7 +287,7 @@ public class ApplesoftCompiler implements ApplesoftTokens {
|
||||||
protected void addAssembly(String label, String mnemonic, String parameter) {
|
protected void addAssembly(String label, String mnemonic, String parameter) {
|
||||||
if (label != null) {
|
if (label != null) {
|
||||||
sourceAssembly.append(label);
|
sourceAssembly.append(label);
|
||||||
sourceAssembly.append(":\n");
|
// sourceAssembly.append(":\n");
|
||||||
}
|
}
|
||||||
if (mnemonic != null) {
|
if (mnemonic != null) {
|
||||||
sourceAssembly.append(" ");
|
sourceAssembly.append(" ");
|
||||||
|
@ -361,6 +383,27 @@ public class ApplesoftCompiler implements ApplesoftTokens {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Variable evaluateNumber() throws CompileException {
|
||||||
|
Variable variable = evaluateExpression();
|
||||||
|
if (variable.isNumber()) {
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
throw new CompileException("A number is required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Answer with the line number label. Used by GOTO and ON expr GOTO
|
||||||
|
* statements.
|
||||||
|
*/
|
||||||
|
protected String getLineNumberLabel() throws CompileException {
|
||||||
|
ApplesoftToken token = nextToken();
|
||||||
|
if (token.isString() && isIntegerNumber(token.getStringValue())) {
|
||||||
|
return "LINE" + token.getStringValue();
|
||||||
|
}
|
||||||
|
throw new CompileException("Expecting a line number but found "
|
||||||
|
+ token.toString());
|
||||||
|
}
|
||||||
|
|
||||||
protected void addLoadByteValue(Variable variable, char register) throws CompileException {
|
protected void addLoadByteValue(Variable variable, char register) throws CompileException {
|
||||||
if (variable.isConstantInteger()) {
|
if (variable.isConstantInteger()) {
|
||||||
addAssembly(null, "LD" + register, "#" + variable.getValue());
|
addAssembly(null, "LD" + register, "#" + variable.getValue());
|
||||||
|
@ -380,8 +423,8 @@ public class ApplesoftCompiler implements ApplesoftTokens {
|
||||||
|
|
||||||
protected void addLoadWordValue(Variable variable, char registerHi, char registerLo) throws CompileException {
|
protected void addLoadWordValue(Variable variable, char registerHi, char registerLo) throws CompileException {
|
||||||
if (variable.isConstantInteger()) {
|
if (variable.isConstantInteger()) {
|
||||||
addAssembly(null, "LD" + registerHi, "#>" + variable.getName());
|
addAssembly(null, "LD" + registerHi, "#>" + variable.getValue());
|
||||||
addAssembly(null, "LD" + registerLo, "#<" + variable.getName());
|
addAssembly(null, "LD" + registerLo, "#<" + variable.getValue());
|
||||||
} else if (variable.isTypeInteger()) {
|
} else if (variable.isTypeInteger()) {
|
||||||
addAssembly(null, "LD" + registerHi, variable.getName() + "+1");
|
addAssembly(null, "LD" + registerHi, variable.getName() + "+1");
|
||||||
addAssembly(null, "LD" + registerLo, variable.getName());
|
addAssembly(null, "LD" + registerLo, variable.getName());
|
||||||
|
@ -403,15 +446,27 @@ public class ApplesoftCompiler implements ApplesoftTokens {
|
||||||
|
|
||||||
protected void addLoadFac(Variable variable) throws CompileException {
|
protected void addLoadFac(Variable variable) throws CompileException {
|
||||||
if (variable.isConstantFloat() || variable.isTypeFloat()) {
|
if (variable.isConstantFloat() || variable.isTypeFloat()) {
|
||||||
addAssembly(null, "LDY", "#>" + variable.getName());
|
addLoadAddress(variable, 'Y', 'A');
|
||||||
addAssembly(null, "LDA", "#<" + variable.getName());
|
|
||||||
addAssembly(null, "JSR","MOVFM");
|
addAssembly(null, "JSR","MOVFM");
|
||||||
|
} else if (variable.isConstantInteger() || variable.isTypeInteger()) {
|
||||||
|
addLoadWordValue(variable, 'A', 'Y');
|
||||||
|
addAssembly(null, "JSR", "GIVAYF");
|
||||||
} else {
|
} else {
|
||||||
throw new CompileException("Unable to convert to load FAC for: "
|
throw new CompileException("Unable to convert to load FAC for: "
|
||||||
+ variable.getName());
|
+ 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 {
|
public void evaluateHTAB() throws CompileException {
|
||||||
addLoadByteValue(evaluateExpression(), 'A');
|
addLoadByteValue(evaluateExpression(), 'A');
|
||||||
addAssembly(null, "STA", "CH");
|
addAssembly(null, "STA", "CH");
|
||||||
|
@ -454,6 +509,88 @@ public class ApplesoftCompiler implements ApplesoftTokens {
|
||||||
token = peekToken();
|
token = peekToken();
|
||||||
} while (token != null && !token.isEndOfCommand());
|
} 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.
|
* Indicates if this string is a number.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user