forked from Apple-2-Tools/jace
Jace IDE can now produce Applesoft basic programs!
This commit is contained in:
parent
feaaceff50
commit
d03dddf9ce
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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>();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user