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.Program;
|
||||||
import jace.ide.CompileResult;
|
import jace.ide.CompileResult;
|
||||||
import jace.ide.LanguageHandler;
|
import jace.ide.LanguageHandler;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author blurry
|
* @author blurry
|
||||||
*/
|
*/
|
||||||
public class ApplesoftHandler implements LanguageHandler<Program> {
|
public class ApplesoftHandler implements LanguageHandler<ApplesoftProgram> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNewDocumentContent() {
|
public String getNewDocumentContent() {
|
||||||
@ -17,13 +21,49 @@ public class ApplesoftHandler implements LanguageHandler<Program> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompileResult<Program> compile(Program program) {
|
public CompileResult<ApplesoftProgram> compile(Program program) {
|
||||||
return null;
|
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
|
@Override
|
||||||
public void execute(CompileResult<Program> lastResult) {
|
public void execute(CompileResult<ApplesoftProgram> lastResult) {
|
||||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
lastResult.getCompiledAsset().run();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package jace.applesoft;
|
package jace.applesoft;
|
||||||
|
|
||||||
|
import jace.Emulator;
|
||||||
import jace.core.RAM;
|
import jace.core.RAM;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
@ -40,6 +41,7 @@ public class ApplesoftProgram {
|
|||||||
|
|
||||||
List<Line> lines = new ArrayList<>();
|
List<Line> lines = new ArrayList<>();
|
||||||
public static final int startingAddressPointer = 0x067;
|
public static final int startingAddressPointer = 0x067;
|
||||||
|
public static final int BASIC_RUN = 0x0e000;
|
||||||
int startingAddress = 0x0801;
|
int startingAddress = 0x0801;
|
||||||
|
|
||||||
public static void main(String... args) {
|
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);
|
out = lines.stream().map((l) -> l.toString() + "\n").reduce(out, String::concat);
|
||||||
return out;
|
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
|
* A command is a list of parts, either raw bytes (ascii text) or tokens. When
|
||||||
* put together they represent a single basic statement.
|
* 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 {
|
public class Command {
|
||||||
|
|
||||||
@ -138,11 +138,32 @@ public class Command {
|
|||||||
LEFT((byte) 0x0E8, "LEFT$"),
|
LEFT((byte) 0x0E8, "LEFT$"),
|
||||||
RIGHT((byte) 0x0E9, "RIGHT$"),
|
RIGHT((byte) 0x0E9, "RIGHT$"),
|
||||||
MID((byte) 0x0EA, "MID$");
|
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 String str;
|
||||||
private byte b;
|
public byte code;
|
||||||
|
|
||||||
TOKEN(byte b, String str) {
|
TOKEN(byte b, String str) {
|
||||||
this.b = b;
|
this.code = b;
|
||||||
this.str = str;
|
this.str = str;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +174,7 @@ public class Command {
|
|||||||
|
|
||||||
public static TOKEN fromByte(byte b) {
|
public static TOKEN fromByte(byte b) {
|
||||||
for (TOKEN t : values()) {
|
for (TOKEN t : values()) {
|
||||||
if (t.b == b) {
|
if (t.code == b) {
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -179,12 +200,25 @@ public class Command {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ByteOrToken(TOKEN token) {
|
||||||
|
isToken = true;
|
||||||
|
this.t = token;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return isToken
|
return isToken
|
||||||
? " " + t.toString() + " "
|
? " " + t.toString() + " "
|
||||||
: String.valueOf((char) b);
|
: String.valueOf((char) b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte getByte() {
|
||||||
|
if (isToken) {
|
||||||
|
return t.code;
|
||||||
|
} else {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
List<ByteOrToken> parts = new ArrayList<ByteOrToken>();
|
List<ByteOrToken> parts = new ArrayList<ByteOrToken>();
|
||||||
|
|
||||||
|
@ -18,21 +18,26 @@
|
|||||||
*/
|
*/
|
||||||
package jace.applesoft;
|
package jace.applesoft;
|
||||||
|
|
||||||
|
import jace.applesoft.Command.TOKEN;
|
||||||
|
import static java.lang.Character.isDigit;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Representation of a line of applesoft basic, having a line number and a list of program commands.
|
* Representation of a line of applesoft basic, having a line number and a list
|
||||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
* of program commands.
|
||||||
|
*
|
||||||
|
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||||
*/
|
*/
|
||||||
public class Line {
|
public class Line {
|
||||||
|
|
||||||
private static char STATEMENT_BREAK = ':'; // delimits multiple commands, the colon character
|
private static final char STATEMENT_BREAK = ':'; // delimits multiple commands, the colon character
|
||||||
private int number;
|
|
||||||
|
private int number = -1;
|
||||||
private Line next;
|
private Line next;
|
||||||
private Line previous;
|
private Line previous;
|
||||||
private List<Command> commands = new ArrayList<>();
|
private List<Command> commands = new ArrayList<>();
|
||||||
private int length;
|
private int length = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the number
|
* @return the number
|
||||||
@ -109,7 +114,9 @@ public class Line {
|
|||||||
String out = String.valueOf(getNumber());
|
String out = String.valueOf(getNumber());
|
||||||
boolean isFirst = true;
|
boolean isFirst = true;
|
||||||
for (Command c : commands) {
|
for (Command c : commands) {
|
||||||
if (!isFirst) out += STATEMENT_BREAK;
|
if (!isFirst) {
|
||||||
|
out += STATEMENT_BREAK;
|
||||||
|
}
|
||||||
out += c.toString();
|
out += c.toString();
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
}
|
}
|
||||||
@ -118,7 +125,7 @@ public class Line {
|
|||||||
|
|
||||||
static Line fromBinary(List<Byte> binary, int pos) {
|
static Line fromBinary(List<Byte> binary, int pos) {
|
||||||
Line l = new Line();
|
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);
|
l.setNumber(lineNumber);
|
||||||
pos += 4;
|
pos += 4;
|
||||||
Command c = new Command();
|
Command c = new Command();
|
||||||
@ -138,4 +145,73 @@ public class Line {
|
|||||||
l.length = size;
|
l.length = size;
|
||||||
return l;
|
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.applesoft.ApplesoftHandler;
|
||||||
import jace.assembly.AssemblyHandler;
|
import jace.assembly.AssemblyHandler;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.BufferedWriter;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
|
Loading…
Reference in New Issue
Block a user