2015-07-15 06:29:37 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com.
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
|
|
* MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
package jace.applesoft;
|
|
|
|
|
2015-07-20 02:40:24 +00:00
|
|
|
import jace.Emulator;
|
2015-07-15 06:29:37 +00:00
|
|
|
import jace.core.RAM;
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileInputStream;
|
|
|
|
import java.io.FileNotFoundException;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.logging.Level;
|
|
|
|
import java.util.logging.Logger;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Decode an applesoft program into a list of program lines Right now this is an
|
|
|
|
* example/test program but it successfully tokenized the source of Lemonade
|
|
|
|
* Stand.
|
|
|
|
*
|
|
|
|
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
|
|
|
*/
|
|
|
|
public class ApplesoftProgram {
|
|
|
|
|
|
|
|
List<Line> lines = new ArrayList<>();
|
|
|
|
public static final int startingAddressPointer = 0x067;
|
2016-11-05 15:46:58 +00:00
|
|
|
public static final int VARIABLE_TABLE = 0x069;
|
|
|
|
public static final int ARRAY_TABLE = 0x06b;
|
|
|
|
public static final int VARIABLE_TABLE_END = 0x06d;
|
2015-07-20 02:40:24 +00:00
|
|
|
public static final int BASIC_RUN = 0x0e000;
|
2015-07-15 06:29:37 +00:00
|
|
|
int startingAddress = 0x0801;
|
|
|
|
|
|
|
|
public static void main(String... args) {
|
|
|
|
byte[] source = null;
|
|
|
|
try {
|
|
|
|
File f = new File("/home/brobert/Documents/Personal/a2gameserver/lib/data/games/LEMONADE#fc0801");
|
|
|
|
FileInputStream in = new FileInputStream(f);
|
|
|
|
source = new byte[(int) f.length()];
|
|
|
|
in.read(source);
|
|
|
|
} catch (FileNotFoundException ex) {
|
|
|
|
Logger.getLogger(ApplesoftProgram.class.getName()).log(Level.SEVERE, null, ex);
|
|
|
|
} catch (IOException ex) {
|
|
|
|
Logger.getLogger(ApplesoftProgram.class.getName()).log(Level.SEVERE, null, ex);
|
|
|
|
}
|
|
|
|
ApplesoftProgram test = ApplesoftProgram.fromBinary(Arrays.asList(toObjects(source)));
|
|
|
|
System.out.println(test.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Byte[] toObjects(byte[] bytesPrim) {
|
|
|
|
Byte[] bytes = new Byte[bytesPrim.length];
|
|
|
|
Arrays.setAll(bytes, n -> bytesPrim[n]);
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static ApplesoftProgram fromMemory(RAM memory) {
|
|
|
|
int startAddress = memory.readWordRaw(startingAddressPointer);
|
|
|
|
int nextCheck = memory.readWordRaw(startAddress);
|
|
|
|
int pos = startAddress;
|
|
|
|
List<Byte> bytes = new ArrayList<>();
|
|
|
|
while (nextCheck != 0) {
|
|
|
|
while (pos < nextCheck + 2) {
|
|
|
|
bytes.add(memory.readRaw(pos++));
|
|
|
|
}
|
|
|
|
nextCheck = memory.readWordRaw(nextCheck);
|
|
|
|
}
|
|
|
|
return fromBinary(bytes, startAddress);
|
|
|
|
}
|
|
|
|
|
2015-07-24 07:32:15 +00:00
|
|
|
public static ApplesoftProgram fromBinary(List<Byte> binary) {
|
|
|
|
return fromBinary(binary, 0x0801);
|
|
|
|
}
|
|
|
|
|
2015-07-15 06:29:37 +00:00
|
|
|
public static ApplesoftProgram fromBinary(List<Byte> binary, int startAddress) {
|
|
|
|
ApplesoftProgram program = new ApplesoftProgram();
|
|
|
|
int currentAddress = startAddress;
|
|
|
|
int pos = 0;
|
|
|
|
while (pos < binary.size()) {
|
|
|
|
int nextAddress = (binary.get(pos) & 0x0ff) + ((binary.get(pos + 1) & 0x0ff) << 8);
|
|
|
|
if (nextAddress == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
int length = nextAddress - currentAddress;
|
|
|
|
Line l = Line.fromBinary(binary, pos);
|
|
|
|
if (l == null) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
program.lines.add(l);
|
|
|
|
if (l.getLength() != length) {
|
|
|
|
System.out.println("Line " + l.getNumber() + " parsed as " + l.getLength() + " bytes long, but that leaves "
|
|
|
|
+ (length - l.getLength()) + " bytes hidden behind next line");
|
|
|
|
}
|
|
|
|
pos += length;
|
|
|
|
currentAddress = nextAddress;
|
|
|
|
}
|
|
|
|
return program;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
String out = "";
|
|
|
|
out = lines.stream().map((l) -> l.toString() + "\n").reduce(out, String::concat);
|
|
|
|
return out;
|
|
|
|
}
|
2016-11-05 15:46:58 +00:00
|
|
|
|
2015-07-20 02:40:24 +00:00
|
|
|
public static ApplesoftProgram fromString(String programSource) {
|
|
|
|
ApplesoftProgram program = new ApplesoftProgram();
|
|
|
|
for (String line : programSource.split("\\n")) {
|
2016-11-05 15:46:58 +00:00
|
|
|
if (line.trim().isEmpty()) {
|
|
|
|
continue;
|
|
|
|
}
|
2015-07-20 02:40:24 +00:00
|
|
|
program.lines.add(Line.fromString(line));
|
|
|
|
}
|
|
|
|
//correct line linkage
|
2016-11-05 15:46:58 +00:00
|
|
|
for (int i = 0; i < program.lines.size(); i++) {
|
2015-07-20 02:40:24 +00:00
|
|
|
if (i > 0) {
|
2016-11-05 15:46:58 +00:00
|
|
|
program.lines.get(i).setPrevious(program.lines.get(i - 1));
|
2015-07-20 02:40:24 +00:00
|
|
|
}
|
2016-11-05 15:46:58 +00:00
|
|
|
if (i < program.lines.size() - 1) {
|
|
|
|
program.lines.get(i).setNext(program.lines.get(i + 1));
|
2015-07-20 02:40:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return program;
|
2016-11-05 15:46:58 +00:00
|
|
|
}
|
2015-07-20 02:40:24 +00:00
|
|
|
|
|
|
|
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;
|
2016-11-05 15:46:58 +00:00
|
|
|
memory.writeWord(pos, nextPos, false, true);
|
|
|
|
pos += 2;
|
|
|
|
memory.writeWord(pos, line.getNumber(), false, true);
|
|
|
|
pos += 2;
|
2015-07-20 02:40:24 +00:00
|
|
|
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);
|
2016-11-05 15:46:58 +00:00
|
|
|
clearVariables(pos);
|
2015-07-20 02:40:24 +00:00
|
|
|
// Emulator.computer.cpu.setProgramCounter(BASIC_RUN);
|
|
|
|
Emulator.computer.resume();
|
|
|
|
}
|
2016-11-05 15:46:58 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Rough approximation of the CLEAR command at $D66A.
|
|
|
|
* http://www.txbobsc.com/scsc/scdocumentor/D52C.html
|
|
|
|
* @param programEnd Program ending address
|
|
|
|
*/
|
|
|
|
private void clearVariables(int programEnd) {
|
|
|
|
RAM memory = Emulator.computer.memory;
|
|
|
|
memory.writeWord(ARRAY_TABLE, programEnd, false, true);
|
|
|
|
memory.writeWord(VARIABLE_TABLE, programEnd, false, true);
|
|
|
|
memory.writeWord(VARIABLE_TABLE_END, programEnd, false, true);
|
|
|
|
}
|
2015-07-15 06:29:37 +00:00
|
|
|
}
|