diff --git a/src/main/java/jace/applesoft/ApplesoftHandler.java b/src/main/java/jace/applesoft/ApplesoftHandler.java index a261816..74cc154 100644 --- a/src/main/java/jace/applesoft/ApplesoftHandler.java +++ b/src/main/java/jace/applesoft/ApplesoftHandler.java @@ -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 { +public class ApplesoftHandler implements LanguageHandler { @Override public String getNewDocumentContent() { @@ -17,13 +21,49 @@ public class ApplesoftHandler implements LanguageHandler { } @Override - public CompileResult compile(Program program) { - return null; + public CompileResult compile(Program program) { + final ApplesoftProgram result = ApplesoftProgram.fromString(program.getValue()); + final Map warnings = new LinkedHashMap<>(); +// int lineNumber = 1; +// for (Line l : result.lines) { +// warnings.put(lineNumber++, l.toString()); +// } + return new CompileResult() { + @Override + public boolean isSuccessful() { + return result != null; + } + + @Override + public ApplesoftProgram getCompiledAsset() { + return result; + } + + @Override + public Map getErrors() { + return Collections.EMPTY_MAP; + } + + @Override + public Map getWarnings() { + return warnings; + } + + @Override + public List getOtherMessages() { + return Collections.EMPTY_LIST; + } + + @Override + public List getRawOutput() { + return Collections.EMPTY_LIST; + } + }; } @Override - public void execute(CompileResult lastResult) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + public void execute(CompileResult lastResult) { + lastResult.getCompiledAsset().run(); } } diff --git a/src/main/java/jace/applesoft/ApplesoftProgram.java b/src/main/java/jace/applesoft/ApplesoftProgram.java index 7475239..6d6c98f 100755 --- a/src/main/java/jace/applesoft/ApplesoftProgram.java +++ b/src/main/java/jace/applesoft/ApplesoftProgram.java @@ -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 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(); + } } diff --git a/src/main/java/jace/applesoft/Command.java b/src/main/java/jace/applesoft/Command.java index d5759e3..ec006af 100755 --- a/src/main/java/jace/applesoft/Command.java +++ b/src/main/java/jace/applesoft/Command.java @@ -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 parts = new ArrayList(); diff --git a/src/main/java/jace/applesoft/Line.java b/src/main/java/jace/applesoft/Line.java index 3926b58..103c5cd 100755 --- a/src/main/java/jace/applesoft/Line.java +++ b/src/main/java/jace/applesoft/Line.java @@ -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 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 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; } -} \ No newline at end of file + + 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; + } + +} diff --git a/src/main/java/jace/ide/Program.java b/src/main/java/jace/ide/Program.java index 405e7ae..f63fb8d 100644 --- a/src/main/java/jace/ide/Program.java +++ b/src/main/java/jace/ide/Program.java @@ -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;