Jace IDE can now produce Applesoft basic programs!

This commit is contained in:
Brendan Robert 2015-07-19 21:40:24 -05:00
parent feaaceff50
commit d03dddf9ce
5 changed files with 218 additions and 18 deletions

View File

@ -4,12 +4,16 @@ import jace.Emulator;
import jace.ide.Program;
import jace.ide.CompileResult;
import jace.ide.LanguageHandler;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
*
* @author blurry
*/
public class ApplesoftHandler implements LanguageHandler<Program> {
public class ApplesoftHandler implements LanguageHandler<ApplesoftProgram> {
@Override
public String getNewDocumentContent() {
@ -17,13 +21,49 @@ public class ApplesoftHandler implements LanguageHandler<Program> {
}
@Override
public CompileResult<Program> compile(Program program) {
return null;
public CompileResult<ApplesoftProgram> compile(Program program) {
final ApplesoftProgram result = ApplesoftProgram.fromString(program.getValue());
final Map<Integer, String> warnings = new LinkedHashMap<>();
// int lineNumber = 1;
// for (Line l : result.lines) {
// warnings.put(lineNumber++, l.toString());
// }
return new CompileResult<ApplesoftProgram>() {
@Override
public boolean isSuccessful() {
return result != null;
}
@Override
public ApplesoftProgram getCompiledAsset() {
return result;
}
@Override
public Map<Integer, String> getErrors() {
return Collections.EMPTY_MAP;
}
@Override
public Map<Integer, String> getWarnings() {
return warnings;
}
@Override
public List<String> getOtherMessages() {
return Collections.EMPTY_LIST;
}
@Override
public List<String> getRawOutput() {
return Collections.EMPTY_LIST;
}
};
}
@Override
public void execute(CompileResult<Program> lastResult) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
public void execute(CompileResult<ApplesoftProgram> lastResult) {
lastResult.getCompiledAsset().run();
}
}

View File

@ -18,6 +18,7 @@
*/
package jace.applesoft;
import jace.Emulator;
import jace.core.RAM;
import java.io.File;
import java.io.FileInputStream;
@ -40,6 +41,7 @@ public class ApplesoftProgram {
List<Line> lines = new ArrayList<>();
public static final int startingAddressPointer = 0x067;
public static final int BASIC_RUN = 0x0e000;
int startingAddress = 0x0801;
public static void main(String... args) {
@ -113,4 +115,53 @@ public class ApplesoftProgram {
out = lines.stream().map((l) -> l.toString() + "\n").reduce(out, String::concat);
return out;
}
public static ApplesoftProgram fromString(String programSource) {
ApplesoftProgram program = new ApplesoftProgram();
for (String line : programSource.split("\\n")) {
if (line.trim().isEmpty()) continue;
program.lines.add(Line.fromString(line));
}
//correct line linkage
for (int i=0; i < program.lines.size(); i++) {
if (i > 0) {
program.lines.get(i).setPrevious(program.lines.get(i-1));
}
if (i < program.lines.size()-1) {
program.lines.get(i).setNext(program.lines.get(i+1));
}
}
return program;
};
public void run() {
RAM memory = Emulator.computer.memory;
Emulator.computer.pause();
int pos = memory.readWordRaw(startingAddressPointer);
for (Line line : lines) {
int nextPos = pos + line.getLength() + 1;
memory.write(pos++, (byte) (nextPos & 0x0ff), false, true);
memory.write(pos++, (byte) (nextPos>>8 & 0x0ff), false, true);
memory.write(pos++, (byte) (line.getNumber() & 0x0ff), false, true);
memory.write(pos++, (byte) (line.getNumber() >> 8 & 0x0ff), false, true);
boolean isFirst = true;
for (Command command : line.getCommands()) {
if (!isFirst) {
memory.write(pos++, (byte) ':', false, true);
}
isFirst = false;
for (Command.ByteOrToken part : command.parts) {
memory.write(pos++, part.getByte(), false, true);
}
}
memory.write(pos++, (byte) 0, false, true);
}
memory.write(pos++, (byte) 0, false, true);
memory.write(pos++, (byte) 0, false, true);
memory.write(pos++, (byte) 0, false, true);
memory.write(pos++, (byte) 0, false, true);
// Emulator.computer.cpu.setProgramCounter(BASIC_RUN);
Emulator.computer.resume();
}
}

View File

@ -25,7 +25,7 @@ import java.util.List;
* A command is a list of parts, either raw bytes (ascii text) or tokens. When
* put together they represent a single basic statement.
*
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public class Command {
@ -138,11 +138,32 @@ public class Command {
LEFT((byte) 0x0E8, "LEFT$"),
RIGHT((byte) 0x0E9, "RIGHT$"),
MID((byte) 0x0EA, "MID$");
static TOKEN findMatch(String search, int start) {
for (TOKEN t : values()) {
int i = start;
boolean found = true;
for (int j = 0; j < t.toString().length() && j + i < search.length(); j++) {
while (search.charAt(j + i) == ' ') {
i++;
}
if (i + j >= search.length()
|| (search.charAt(i + j) != t.toString().charAt(j))) {
found = false;
break;
}
}
if (found) {
return t;
}
}
return null;
}
private String str;
private byte b;
public byte code;
TOKEN(byte b, String str) {
this.b = b;
this.code = b;
this.str = str;
}
@ -153,7 +174,7 @@ public class Command {
public static TOKEN fromByte(byte b) {
for (TOKEN t : values()) {
if (t.b == b) {
if (t.code == b) {
return t;
}
}
@ -179,12 +200,25 @@ public class Command {
}
public ByteOrToken(TOKEN token) {
isToken = true;
this.t = token;
}
@Override
public String toString() {
return isToken
? " " + t.toString() + " "
: String.valueOf((char) b);
}
public byte getByte() {
if (isToken) {
return t.code;
} else {
return b;
}
}
}
List<ByteOrToken> parts = new ArrayList<ByteOrToken>();

View File

@ -18,21 +18,26 @@
*/
package jace.applesoft;
import jace.applesoft.Command.TOKEN;
import static java.lang.Character.isDigit;
import java.util.ArrayList;
import java.util.List;
/**
* Representation of a line of applesoft basic, having a line number and a list of program commands.
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
* Representation of a line of applesoft basic, having a line number and a list
* of program commands.
*
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public class Line {
private static char STATEMENT_BREAK = ':'; // delimits multiple commands, the colon character
private int number;
private static final char STATEMENT_BREAK = ':'; // delimits multiple commands, the colon character
private int number = -1;
private Line next;
private Line previous;
private List<Command> commands = new ArrayList<>();
private int length;
private int length = 0;
/**
* @return the number
@ -109,7 +114,9 @@ public class Line {
String out = String.valueOf(getNumber());
boolean isFirst = true;
for (Command c : commands) {
if (!isFirst) out += STATEMENT_BREAK;
if (!isFirst) {
out += STATEMENT_BREAK;
}
out += c.toString();
isFirst = false;
}
@ -118,7 +125,7 @@ public class Line {
static Line fromBinary(List<Byte> binary, int pos) {
Line l = new Line();
int lineNumber = (binary.get(pos+2) & 0x0ff) + ((binary.get(pos+3) & 0x0ff) << 8);
int lineNumber = (binary.get(pos + 2) & 0x0ff) + ((binary.get(pos + 3) & 0x0ff) << 8);
l.setNumber(lineNumber);
pos += 4;
Command c = new Command();
@ -138,4 +145,73 @@ public class Line {
l.length = size;
return l;
}
}
static Line fromString(String lineString) {
Line l = new Line();
boolean inString = false;
boolean hasLineNumber = false;
boolean isComment = false;
Command currentCommand = new Command();
l.commands.add(currentCommand);
l.length = 4;
lineString = lineString.trim();
String upperLineString = lineString.toUpperCase();
for (int i = 0; i < lineString.length(); i++) {
if (!hasLineNumber) {
int lineNumber = 0;
for (; i < lineString.length() && isDigit(lineString.charAt(i)); i++) {
lineNumber = lineNumber * 10 + lineString.charAt(i) - '0';
}
i--;
l.setNumber(lineNumber);
hasLineNumber = true;
} else if (inString || isComment) {
if (!isComment && lineString.charAt(i) == '"') {
inString = false;
}
currentCommand.parts.add(new Command.ByteOrToken((byte) lineString.charAt(i)));
l.length++;
} else if (lineString.charAt(i) == '"') {
inString = true;
currentCommand.parts.add(new Command.ByteOrToken((byte) lineString.charAt(i)));
l.length++;
} else if (lineString.charAt(i) == STATEMENT_BREAK) {
currentCommand = new Command();
l.commands.add(currentCommand);
l.length++;
} else if (lineString.charAt(i) == '?') {
Command.ByteOrToken part = new Command.ByteOrToken(TOKEN.PRINT);
currentCommand.parts.add(part);
l.length++;
} else {
TOKEN match = Command.TOKEN.findMatch(upperLineString, i);
if (match != null) {
if (match == TOKEN.REM) {
isComment = true;
}
Command.ByteOrToken part = new Command.ByteOrToken(match);
currentCommand.parts.add(part);
for (int j=0; j+i <= match.toString().length(); j++) {
while (lineString.charAt(i+j) == ' ') {
i++;
}
}
i += match.toString().length() - 1;
if (isComment) {
while (i+1 < lineString.length() && lineString.charAt(i+1) == ' ') {
i++;
}
}
l.length++;
} else {
if (lineString.charAt(i) != ' ') {
currentCommand.parts.add(new Command.ByteOrToken((byte) upperLineString.charAt(i)));
l.length++;
}
}
}
}
return l;
}
}

View File

@ -3,7 +3,6 @@ package jace.ide;
import jace.applesoft.ApplesoftHandler;
import jace.assembly.AssemblyHandler;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;