Merge branch 'master' of github.com:badvision/lawless-legends

This commit is contained in:
Martin Haye 2021-04-20 08:02:33 -07:00
commit 21312acdb8
26 changed files with 546 additions and 253 deletions

View File

@ -5,7 +5,7 @@
<artifactId>OutlawEditor</artifactId>
<name>OutlawEditor</name>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<version>2.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<mainClass>org.badvision.outlaweditor.Application</mainClass>
@ -22,7 +22,7 @@
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
<version>1.24.0</version>
<version>1.26.4</version>
<executions>
<execution>
<id>generate-scr-scrdescriptor</id>
@ -40,7 +40,7 @@
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>3.3.0</version>
<version>5.1.2</version>
<extensions>true</extensions>
<configuration>
<instructions>
@ -52,7 +52,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<version>3.1.2</version>
<executions>
<execution>
<id>unpack-dependencies</id>
@ -79,7 +79,7 @@
</manifest>
</archive>
</configuration>
<version>3.0.2</version>
<version>3.2.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@ -88,12 +88,12 @@
<source>1.8</source>
<target>1.8</target>
</configuration>
<version>3.6.1</version>
<version>3.8.1</version>
</plugin>
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.13.2</version>
<version>0.14.0</version>
<executions>
<execution>
<goals>
@ -113,7 +113,7 @@
<plugin>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics</artifactId>
<version>0.11.1</version>
<version>0.12.0</version>
</plugin>
</plugins>
</configuration>
@ -128,24 +128,24 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<version>4.13.2</version>
<scope>test</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics</artifactId>
<version>0.11.1</version>
<version>0.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.framework</artifactId>
<version>5.6.4</version>
<version>5.6.10</version>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.main</artifactId>
<version>5.6.4</version>
<version>5.6.10</version>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
@ -155,22 +155,22 @@
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr</artifactId>
<version>2.0.12</version>
<version>2.1.26</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.16</version>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.16</version>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>org.controlsfx</groupId>
<artifactId>controlsfx</artifactId>
<version>8.40.15</version>
<version>8.40.18</version>
</dependency>
</dependencies>
</project>

View File

@ -35,8 +35,6 @@ import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.input.Clipboard;
import javafx.scene.input.DataFormat;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Pane;
@ -72,6 +70,7 @@ public class MapEditor extends Editor<Map, MapEditor.DrawMode> implements EventH
double tileWidth = getCurrentPlatform().tileRenderer.getWidth() * zoom;
double tileHeight = getCurrentPlatform().tileRenderer.getHeight() * zoom;
Color cursorAssistColor = new Color(0.2, 0.2, 1.0, 0.4);
Script highlightedScript = null;
@Override
protected void onEntityUpdated() {
@ -134,7 +133,7 @@ public class MapEditor extends Editor<Map, MapEditor.DrawMode> implements EventH
getCurrentMap().shift(amt, 0);
redraw();
}
@Override
public void buildEditorUI(Pane tileEditorAnchorPane) {
anchorPane = tileEditorAnchorPane;
@ -192,6 +191,7 @@ public class MapEditor extends Editor<Map, MapEditor.DrawMode> implements EventH
public void setSelectedScript(Script script) {
selectedScript = script;
highlightedScript = script;
}
public Script getSelectedScript() {
@ -314,7 +314,7 @@ public class MapEditor extends Editor<Map, MapEditor.DrawMode> implements EventH
}
for (int x = 0; x <= cols; x++) {
for (int y = 0; y <= rows; y++) {
if (highlightScripts(x, y, currentMap.getLocationScripts(posX + x, posY + y))) {
if (highlightScripts(x, y, currentMap.getLocationScripts(posX + x, posY + y), highlightedScript)) {
tiles[x][y] = "SCRIPT";
}
}
@ -391,7 +391,7 @@ public class MapEditor extends Editor<Map, MapEditor.DrawMode> implements EventH
}
}
private boolean highlightScripts(int x, int y, List<Script> scripts) {
private boolean highlightScripts(int x, int y, List<Script> scripts, Script extraHighlight) {
if (scripts == null || scripts.isEmpty()) {
return false;
}
@ -400,6 +400,14 @@ public class MapEditor extends Editor<Map, MapEditor.DrawMode> implements EventH
return false;
}
GraphicsContext gc = drawCanvas.getGraphicsContext2D();
if (highlightedScript != null && visibleScripts.contains(extraHighlight)) {
Color color = currentMap.getScriptColor(extraHighlight).get().brighter();
color = Color.color(color.getRed(), color.getGreen(), color.getBlue(), 0.5);
gc.setFill(color);
double xx = x * tileWidth;
double yy = y * tileHeight;
gc.fillRect(xx, yy, tileWidth, tileHeight);
}
int idx = 0;
double xx = x * tileWidth;
double yy = y * tileHeight;
@ -499,8 +507,8 @@ public class MapEditor extends Editor<Map, MapEditor.DrawMode> implements EventH
public void copyScript(Script s) {
java.util.Map<DataFormat, Object> clip = new HashMap<>();
clip.put(DataFormat.PLAIN_TEXT, "selection/map/" +
ApplicationState.getInstance().getGameData().getMap().indexOf(getEntity()) +
clip.put(DataFormat.PLAIN_TEXT, "selection/map/" +
ApplicationState.getInstance().getGameData().getMap().indexOf(getEntity()) +
"/script/" + getEntity().getScripts().getScript().indexOf(s));
Clipboard.getSystemClipboard().setContent(clip);
}

View File

@ -307,13 +307,13 @@ public class DataUtilities {
public static String getStringValueFromCell(Cell cell) {
switch (cell.getCellType()) {
case Cell.CELL_TYPE_BOOLEAN:
case BOOLEAN:
return Boolean.toString(cell.getBooleanCellValue());
case Cell.CELL_TYPE_BLANK:
case BLANK:
return null;
case Cell.CELL_TYPE_NUMERIC:
case NUMERIC:
return Double.toString(cell.getNumericCellValue());
case Cell.CELL_TYPE_STRING:
case STRING:
return cell.getStringCellValue();
default:
return "???";

View File

@ -401,6 +401,7 @@ public class MapEditorTabControllerImpl extends MapEditorTabController {
} else {
getCurrentEditor().setSelectedScript(newValue);
}
getCurrentEditor().redraw();
}
});
scriptEraseTool.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> {

View File

@ -72,6 +72,8 @@ public class SheetEditorControllerImpl extends SheetEditorController {
createMenuItem("Insert Row", () -> insertRow(new Row(), getSelectedRow())),
createMenuItem("Clone Row", () -> cloneRow(editor.getSheet().getRows().getRow().get(getSelectedRow()))),
createMenuItem("Delete Row", () -> deleteRowWithConfirmation(editor.getSheet().getRows().getRow().get(getSelectedRow()))),
createMenuItem("Delete Column", () -> deleteColumnWithConfirmation(editor.getSheet().getColumns().getColumn().get(getSelectedColumn()))),
createMenuItem("Rename Column", () -> renameColumn(editor.getSheet().getColumns().getColumn().get(getSelectedColumn()))),
createMenuItem("Sort ascending", () -> {
int sortCol = table.getSelectionModel().getFocusedCell().getColumn();
table.setComparator((a,b)->compare(getCellFromRow(a, sortCol), getCellFromRow(b, sortCol)));
@ -99,6 +101,10 @@ public class SheetEditorControllerImpl extends SheetEditorController {
return table.getSelectionModel().getFocusedCell().getRow();
}
private int getSelectedColumn() {
return table.getSelectionModel().getFocusedCell().getColumn();
}
@Override
public void doImport(ActionEvent event) {
FileChooser openFileDialog = new FileChooser();
@ -116,12 +122,13 @@ public class SheetEditorControllerImpl extends SheetEditorController {
}).collect(Collectors.toCollection(editor.getSheet().getColumns()::getColumn));
editor.getSheet().setRows(new Rows());
List<String> header = data.get(0);
data.stream().skip(1)
.map(cols -> {
Row r = new Row();
for (int i = 0; i < cols.size(); i++) {
if (cols.get(i) != null) {
setValue(r.getOtherAttributes(), data.get(0).get(i), cols.get(i));
if (cols.get(i) != null && header.size() > i && header.get(i) != null) {
setValue(r.getOtherAttributes(), header.get(i), cols.get(i));
}
}
return r;

View File

@ -4,7 +4,7 @@
<groupId>org.8bitbunch</groupId>
<artifactId>lawlesslegends</artifactId>
<version>2.0-SNAPSHOT</version>
<version>3.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>lawlesslegends</name>
@ -26,7 +26,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<version>3.1.2</version>
<executions>
<execution>
<id>unpack-dependencies</id>
@ -45,7 +45,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.4.0</version>
<version>3.0.0</version>
<executions>
<execution>
<id>unpack-dependencies</id>
@ -78,7 +78,7 @@
<source>1.8</source>
<target>1.8</target>
</configuration>
<version>3.5</version>
<version>3.8.1</version>
</plugin>
</plugins>
</build>
@ -87,14 +87,19 @@
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.9</version>
<version>0.9.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.collections</groupId>
<artifactId>eclipse-collections</artifactId>
<version>10.4.0</version>
</dependency>
<dependency>
<groupId>org.xerial.thirdparty</groupId>
<artifactId>nestedvm</artifactId>

View File

@ -5,6 +5,7 @@
*/
package jace;
import jace.apple2e.SoftSwitches;
import jace.apple2e.VideoNTSC;
import jace.config.Configuration;
import jace.core.RAMEvent;
@ -18,9 +19,6 @@ import jace.lawless.LawlessHacks;
import jace.lawless.LawlessImageTool;
import jace.lawless.LawlessVideo;
import jace.ui.MetacheatUI;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
@ -31,6 +29,10 @@ import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author blurry
@ -43,6 +45,7 @@ public class LawlessLegends extends Application {
public JaceUIController controller;
static boolean romStarted = false;
static public boolean PRODUCTION_MODE = true;
@Override
public void start(Stage stage) throws Exception {
@ -126,21 +129,26 @@ public class LawlessLegends extends Application {
*/
private void bootWatchdog() {
romStarted = false;
RAMListener startListener = Emulator.computer.getMemory().
observe(RAMEvent.TYPE.EXECUTE, 0x0c700, (e) -> {
romStarted = true;
});
Emulator.computer.invokeColdStart();
try {
Thread.sleep(7500);
if (!romStarted) {
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Boot not detected, performing a cold start");
Emulator.computer.invokeColdStart();
if (PRODUCTION_MODE) {
RAMListener startListener = Emulator.computer.getMemory().
observe(RAMEvent.TYPE.EXECUTE, 0x0c700, (e) -> {
romStarted = true;
});
Emulator.computer.invokeColdStart();
try {
Thread.sleep(7500);
if (!romStarted) {
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Boot not detected, performing a cold start");
Emulator.computer.invokeColdStart();
}
} catch (InterruptedException ex) {
Logger.getLogger(LawlessLegends.class.getName()).log(Level.SEVERE, null, ex);
}
} catch (InterruptedException ex) {
Logger.getLogger(LawlessLegends.class.getName()).log(Level.SEVERE, null, ex);
Emulator.computer.getMemory().removeListener(startListener);
} else {
romStarted = true;
Emulator.computer.invokeColdStart();
}
Emulator.computer.getMemory().removeListener(startListener);
}
private void configureEmulatorForGame() {
@ -151,15 +159,23 @@ public class LawlessLegends extends Application {
Emulator.computer.enableStateManager = false;
Emulator.computer.ramCard.setValue(CardRamworks.class);
Emulator.computer.videoRenderer.setValue(LawlessVideo.class);
Emulator.computer.card7.setValue(CardMassStorage.class);
Emulator.computer.card6.setValue(CardDiskII.class);
Emulator.computer.card5.setValue(CardRamFactor.class);
Emulator.computer.card4.setValue(null);
Emulator.computer.card2.setValue(null);
if (PRODUCTION_MODE) {
Emulator.computer.card7.setValue(CardMassStorage.class);
Emulator.computer.card6.setValue(CardDiskII.class);
Emulator.computer.card5.setValue(CardRamFactor.class);
Emulator.computer.card4.setValue(null);
Emulator.computer.card2.setValue(null);
}
Emulator.computer.cheatEngine.setValue(LawlessHacks.class);
Configuration.buildTree();
Emulator.computer.reconfigure();
VideoNTSC.setVideoMode(VideoNTSC.VideoMode.TextFriendly, false);
((LawlessImageTool) Emulator.computer.getUpgradeHandler()).loadGame();
if (PRODUCTION_MODE) {
((LawlessImageTool) Emulator.computer.getUpgradeHandler()).loadGame();
} else {
for (SoftSwitches s : SoftSwitches.values()) {
s.getSwitch().reset();
}
}
}
}

View File

@ -291,8 +291,11 @@ public class Apple2e extends Computer {
loadRom("jace/data/apple2e.rom");
}
RAM128k ram = (RAM128k) getMemory();
ram.activeRead.writeByte(0x0fffc, (byte) 0x000);
ram.activeRead.writeByte(0x0fffd, (byte) 0x0c7);
if (LawlessLegends.PRODUCTION_MODE) {
// Force Slot 7 boot
ram.activeRead.writeByte(0x0fffc, (byte) 0x000);
ram.activeRead.writeByte(0x0fffd, (byte) 0x0c7);
}
if (getVideo() == null || getVideo().getClass() != videoRenderer.getValue()) {
if (getVideo() != null) {

View File

@ -44,6 +44,7 @@ abstract public class RAM128k extends RAM {
Logger LOG = Logger.getLogger(RAM128k.class.getName());
Map<String, PagedMemory> banks;
Map<String, PagedMemory> memoryConfigurations = new HashMap<>();
String state = "???";
private Map<String, PagedMemory> getBanks() {
@ -90,8 +91,8 @@ abstract public class RAM128k extends RAM {
// 64 da : Dump all memory mappings
System.out.println("Active banks");
for (int i = 0; i < 256; i++) {
byte[] read = activeRead.get(i);
byte[] write = activeWrite.get(i);
byte[] read = this.activeRead.get(i);
byte[] write = this.activeWrite.get(i);
String readBank = getBanks().keySet().stream().filter(bank -> {
PagedMemory mem = getBanks().get(bank);
for (byte[] page : mem.getMemory()) {
@ -162,148 +163,250 @@ abstract public class RAM128k extends RAM {
private final Semaphore configurationSemaphone = new Semaphore(1, true);
public String getReadConfiguration() {
String rstate = "";
if (SoftSwitches.RAMRD.getState()) {
rstate += "Ra";
} else {
rstate += "R0";
}
String LCR = "L0R";
if (SoftSwitches.LCRAM.isOn()) {
if (SoftSwitches.AUXZP.isOff()) {
LCR = "L1R";
if (SoftSwitches.LCBANK1.isOff()) {
LCR = "L2R";
}
} else {
LCR = "L1aR";
if (SoftSwitches.LCBANK1.isOff()) {
LCR = "L2aR";
}
}
}
rstate += LCR;
if (SoftSwitches.CXROM.getState()) {
rstate += "CXROM";
} else {
rstate += "!CX";
if (SoftSwitches.SLOTC3ROM.isOff()) {
rstate += "C3";
}
if (SoftSwitches.INTC8ROM.isOn()) {
rstate += "C8";
} else {
rstate += String.format("C8%d", getActiveSlot());
}
}
return rstate;
}
public String getWriteConfiguration() {
String wstate = "";
if (SoftSwitches.RAMWRT.getState()) {
wstate += "Wa";
} else {
wstate += "W0";
}
String LCW = "L0W";
if (SoftSwitches.LCWRITE.isOn()) {
if (SoftSwitches.AUXZP.isOff()) {
LCW = "L1W";
if (SoftSwitches.LCBANK1.isOff()) {
LCW = "L2W";
}
} else {
LCW = "L1aW";
if (SoftSwitches.LCBANK1.isOff()) {
LCW = "L2aW";
}
}
}
wstate += LCW;
return wstate;
}
public String getAuxZPConfiguration() {
String astate = "";
if (SoftSwitches._80STORE.isOn()) {
astate += "80S";
if (SoftSwitches.PAGE2.isOn()) {
astate += "2";
}
if (SoftSwitches.HIRES.isOn()) {
astate += "H";
}
}
// Handle zero-page bankswitching
if (SoftSwitches.AUXZP.getState()) {
astate += "Za";
} else {
astate += "Z0";
}
return astate;
}
public PagedMemory buildReadConfiguration() {
PagedMemory read = new PagedMemory(0x10000, PagedMemory.Type.RAM, computer);
// First off, set up read/write for main memory (might get changed later on)
read.fillBanks(SoftSwitches.RAMRD.getState() ? getAuxMemory() : mainMemory);
// Handle language card softswitches
read.fillBanks(rom);
if (SoftSwitches.LCRAM.isOn()) {
if (SoftSwitches.AUXZP.isOff()) {
read.fillBanks(languageCard);
if (SoftSwitches.LCBANK1.isOff()) {
read.fillBanks(languageCard2);
}
} else {
read.fillBanks(getAuxLanguageCard());
if (SoftSwitches.LCBANK1.isOff()) {
read.fillBanks(getAuxLanguageCard2());
}
}
}
// Handle 80STORE logic for bankswitching video ram
if (SoftSwitches._80STORE.isOn()) {
read.setBanks(0x04, 0x04, 0x04,
SoftSwitches.PAGE2.isOn() ? getAuxMemory() : mainMemory);
if (SoftSwitches.HIRES.isOn()) {
read.setBanks(0x020, 0x020, 0x020,
SoftSwitches.PAGE2.isOn() ? getAuxMemory() : mainMemory);
}
}
// Handle zero-page bankswitching
if (SoftSwitches.AUXZP.getState()) {
// Aux pages 0 and 1
read.setBanks(0, 2, 0, getAuxMemory());
} else {
// Main pages 0 and 1
read.setBanks(0, 2, 0, mainMemory);
}
/*
INTCXROM SLOTC3ROM C1,C2,C4-CF C3
0 0 slot rom
0 1 slot slot
1 - rom rom
*/
if (SoftSwitches.CXROM.getState()) {
// Enable C1-CF to point to rom
read.setBanks(0, 0x0F, 0x0C1, cPageRom);
} else {
// Enable C1-CF to point to slots
for (int slot = 1; slot <= 7; slot++) {
PagedMemory page = getCard(slot).map(Card::getCxRom).orElse(blank);
read.setBanks(0, 1, 0x0c0 + slot, page);
}
if (getActiveSlot() == 0) {
for (int i = 0x0C8; i < 0x0D0; i++) {
read.set(i, blank.get(0));
}
} else {
getCard(getActiveSlot()).ifPresent(c -> read.setBanks(0, 8, 0x0c8, c.getC8Rom()));
}
if (SoftSwitches.SLOTC3ROM.isOff()) {
// Enable C3 to point to internal ROM
read.setBanks(2, 1, 0x0C3, cPageRom);
}
if (SoftSwitches.INTC8ROM.isOn()) {
// Enable C8-CF to point to internal ROM
read.setBanks(7, 8, 0x0C8, cPageRom);
}
}
// All ROM reads not intecepted will return 0xFF! (TODO: floating bus)
read.set(0x0c0, blank.get(0));
return read;
}
public PagedMemory buildWriteConfiguration() {
PagedMemory write = new PagedMemory(0x10000, PagedMemory.Type.RAM, computer);
// First off, set up read/write for main memory (might get changed later on)
write.fillBanks(SoftSwitches.RAMWRT.getState() ? getAuxMemory() : mainMemory);
// Handle language card softswitches
for (int i = 0x0c0; i < 0x0d0; i++) {
write.set(i, null);
}
if (SoftSwitches.LCWRITE.isOn()) {
if (SoftSwitches.AUXZP.isOff()) {
write.fillBanks(languageCard);
if (SoftSwitches.LCBANK1.isOff()) {
write.fillBanks(languageCard2);
}
} else {
write.fillBanks(getAuxLanguageCard());
if (SoftSwitches.LCBANK1.isOff()) {
write.fillBanks(getAuxLanguageCard2());
}
}
} else {
// Make 0xd000 - 0xffff non-writable!
for (int i = 0x0d0; i < 0x0100; i++) {
write.set(i, null);
}
}
// Handle 80STORE logic for bankswitching video ram
if (SoftSwitches._80STORE.isOn()) {
write.setBanks(0x04, 0x04, 0x04,
SoftSwitches.PAGE2.isOn() ? getAuxMemory() : mainMemory);
if (SoftSwitches.HIRES.isOn()) {
write.setBanks(0x020, 0x020, 0x020,
SoftSwitches.PAGE2.isOn() ? getAuxMemory() : mainMemory);
}
}
// Handle zero-page bankswitching
if (SoftSwitches.AUXZP.getState()) {
// Aux pages 0 and 1
write.setBanks(0, 2, 0, getAuxMemory());
} else {
// Main pages 0 and 1
write.setBanks(0, 2, 0, mainMemory);
}
return write;
}
/**
*
*/
@Override
public void configureActiveMemory() {
String auxZpConfiguration = getAuxZPConfiguration();
String readConfiguration = getReadConfiguration() + auxZpConfiguration;
String writeConfiguration = getWriteConfiguration() + auxZpConfiguration;
String newState = readConfiguration + ";" + writeConfiguration;
if (newState.equals(state)) {
return;
}
state = newState;
try {
state = "";
log("MMU Switches");
configurationSemaphone.acquire();
// First off, set up read/write for main memory (might get changed later on)
if (SoftSwitches.RAMRD.getState()) {
state = "Ra";
} else {
state = "R0";
}
activeRead.fillBanks(SoftSwitches.RAMRD.getState() ? getAuxMemory() : mainMemory);
if (SoftSwitches.RAMWRT.getState()) {
state += "Wa";
} else {
state += "W0";
}
activeWrite.fillBanks(SoftSwitches.RAMWRT.getState() ? getAuxMemory() : mainMemory);
// Handle language card softswitches
activeRead.fillBanks(rom);
//activeRead.fillBanks(cPageRom);
for (int i = 0x0c0; i < 0x0d0; i++) {
activeWrite.set(i, null);
}
String LCR = "L0R";
if (SoftSwitches.LCRAM.isOn()) {
if (SoftSwitches.AUXZP.isOff()) {
LCR = "L1R";
activeRead.fillBanks(languageCard);
if (SoftSwitches.LCBANK1.isOff()) {
LCR = "L2R";
activeRead.fillBanks(languageCard2);
}
} else {
activeRead.fillBanks(getAuxLanguageCard());
LCR = "L1aR";
if (SoftSwitches.LCBANK1.isOff()) {
LCR = "L2aR";
activeRead.fillBanks(getAuxLanguageCard2());
}
}
if (memoryConfigurations.containsKey(readConfiguration)) {
activeRead = memoryConfigurations.get(readConfiguration);
} else {
activeRead = buildReadConfiguration();
memoryConfigurations.put(readConfiguration, activeRead);
}
String LCW = "L0W";
if (SoftSwitches.LCWRITE.isOn()) {
if (SoftSwitches.AUXZP.isOff()) {
LCW = "L1W";
activeWrite.fillBanks(languageCard);
if (SoftSwitches.LCBANK1.isOff()) {
LCW = "L2W";
activeWrite.fillBanks(languageCard2);
}
} else {
activeWrite.fillBanks(getAuxLanguageCard());
LCW = "L1aW";
if (SoftSwitches.LCBANK1.isOff()) {
activeWrite.fillBanks(getAuxLanguageCard2());
LCW = "L2aW";
}
}
if (memoryConfigurations.containsKey(writeConfiguration)) {
activeWrite = memoryConfigurations.get(writeConfiguration);
} else {
// Make 0xd000 - 0xffff non-writable!
for (int i = 0x0d0; i < 0x0100; i++) {
activeWrite.set(i, null);
}
activeWrite = buildWriteConfiguration();
memoryConfigurations.put(writeConfiguration, activeWrite);
}
state += String.format(",%s,%s", LCR, LCW);
// Handle 80STORE logic for bankswitching video ram
if (SoftSwitches._80STORE.isOn()) {
state += ",80S";
if (SoftSwitches.PAGE2.isOn()) {
state += "2";
}
activeRead.setBanks(0x04, 0x04, 0x04,
SoftSwitches.PAGE2.isOn() ? getAuxMemory() : mainMemory);
activeWrite.setBanks(0x04, 0x04, 0x04,
SoftSwitches.PAGE2.isOn() ? getAuxMemory() : mainMemory);
if (SoftSwitches.HIRES.isOn()) {
state += "H";
activeRead.setBanks(0x020, 0x020, 0x020,
SoftSwitches.PAGE2.isOn() ? getAuxMemory() : mainMemory);
activeWrite.setBanks(0x020, 0x020, 0x020,
SoftSwitches.PAGE2.isOn() ? getAuxMemory() : mainMemory);
}
}
// Handle zero-page bankswitching
if (SoftSwitches.AUXZP.getState()) {
state += ",Za";
// Aux pages 0 and 1
activeRead.setBanks(0, 2, 0, getAuxMemory());
activeWrite.setBanks(0, 2, 0, getAuxMemory());
} else {
state += ",Z0";
// Main pages 0 and 1
activeRead.setBanks(0, 2, 0, mainMemory);
activeWrite.setBanks(0, 2, 0, mainMemory);
}
/*
INTCXROM SLOTC3ROM C1,C2,C4-CF C3
0 0 slot rom
0 1 slot slot
1 - rom rom
*/
if (SoftSwitches.CXROM.getState()) {
// Enable C1-CF to point to rom
activeRead.setBanks(0, 0x0F, 0x0C1, cPageRom);
} else {
// Enable C1-CF to point to slots
for (int slot = 1; slot <= 7; slot++) {
PagedMemory page = getCard(slot).map(Card::getCxRom).orElse(blank);
activeRead.setBanks(0, 1, 0x0c0 + slot, page);
}
if (getActiveSlot() == 0) {
for (int i = 0x0C8; i < 0x0D0; i++) {
activeRead.set(i, blank.get(0));
}
} else {
getCard(getActiveSlot()).ifPresent(c -> activeRead.setBanks(0, 8, 0x0c8, c.getC8Rom()));
}
if (SoftSwitches.SLOTC3ROM.isOff()) {
// Enable C3 to point to internal ROM
activeRead.setBanks(2, 1, 0x0C3, cPageRom);
state += ",C30";
}
if (SoftSwitches.INTC8ROM.isOn()) {
state += ",C80";
// Enable C8-CF to point to internal ROM
activeRead.setBanks(7, 8, 0x0C8, cPageRom);
} else {
state += String.format(",C8%d", getActiveSlot());
}
}
// All ROM reads not intecepted will return 0xFF! (TODO: floating bus)
activeRead.set(0x0c0, blank.get(0));
configurationSemaphone.release();
} catch (InterruptedException ex) {
Logger.getLogger(RAM128k.class.getName()).log(Level.SEVERE, null, ex);
@ -349,6 +452,8 @@ abstract public class RAM128k extends RAM {
}
// System.out.println("Finished reading rom with " + inputRom.available() + " bytes left unread!");
//dump();
// Clear cached configurations as we might have outdated references now
memoryConfigurations.clear();
configureActiveMemory();
}
@ -406,5 +511,7 @@ abstract public class RAM128k extends RAM {
languageCard2 = currentMemory.languageCard2;
cards = currentMemory.cards;
activeSlot = currentMemory.activeSlot;
// Clear cached configurations as we might have outdated references now
memoryConfigurations.clear();
}
}

View File

@ -20,12 +20,8 @@ package jace.core;
import jace.state.Stateful;
import jace.config.Reconfigurable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import org.eclipse.collections.api.list.MutableList;
import org.eclipse.collections.impl.factory.Lists;
/**
* Device is a very simple abstraction of any emulation component. A device
@ -35,42 +31,43 @@ import javafx.beans.property.SimpleBooleanProperty;
*
* Depending on the type of device, some special work might be required to
* attach or detach it to the active emulation (such as what should happen when
* a card is inserted or removed from a slot?)
* Created on May 10, 2007, 5:46 PM
* a card is inserted or removed from a slot?) Created on May 10, 2007, 5:46 PM
*
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
@Stateful
public abstract class Device implements Reconfigurable {
protected Computer computer;
private List<Device> children;
private MutableList<Device> children;
private Device() {
children = Collections.synchronizedList(new ArrayList<>());
// TODO: Previously this was synchronized -- confirm this is safe to leave unsynchronized
children = Lists.mutable.<Device>empty();
}
public Device(Computer computer) {
this();
this.computer = computer;
}
// Number of cycles to do nothing (for cpu/video cycle accuracy)
@Stateful
private int waitCycles = 0;
@Stateful
private final BooleanProperty run = new SimpleBooleanProperty(true);
private boolean run = true;
@Stateful
public boolean isPaused = false;
@Stateful
public boolean isAttached = false;
public void addChildDevice(Device d) {
children.add(d);
if (isAttached) {
d.attach();
}
}
public void removeChildDevice(Device d) {
children.remove(d);
d.suspend();
@ -78,19 +75,19 @@ public abstract class Device implements Reconfigurable {
d.detach();
}
}
public void addAllDevices(Collection<Device> devices) {
public void addAllDevices(Iterable<Device> devices) {
devices.forEach(this::addChildDevice);
}
public List<Device> getChildren() {
return Collections.unmodifiableList(children);
public Iterable<Device> getChildren() {
return children.asUnmodifiable();
}
public BooleanProperty getRunningProperty() {
public boolean getRunningProperty() {
return run;
}
public void addWaitCycles(int wait) {
waitCycles += wait;
}
@ -100,38 +97,24 @@ public abstract class Device implements Reconfigurable {
}
public void doTick() {
/*
if (waitCycles <= 0)
tick();
else
waitCycles--;
*/
if (!run.get()) {
// System.out.println("Device stopped: " + getName());
isPaused = true;
return;
}
// The following might be as much as 7% faster than the above
// My guess is that the above results in a GOTO
// whereas the following pre-emptive return avoids that
if (waitCycles > 0) {
if (run) {
children.forEach(Device::tick);
if (waitCycles <= 0) {
tick();
return;
}
waitCycles--;
return;
}
// Implicit else...
children.forEach(Device::doTick);
tick();
}
public boolean isRunning() {
return run.get();
return run;
}
public synchronized void setRun(boolean run) {
// System.out.println(Thread.currentThread().getName() + (run ? " resuming " : " suspending ")+ getDeviceName());
isPaused = false;
this.run.set(run);
this.run = run;
}
protected abstract String getDeviceName();

View File

@ -103,6 +103,11 @@ public class CardRamworks extends RAM128k {
return getAuxBank(BankType.LANGUAGE_CARD_2, currentBank);
}
@Override
public String getAuxZPConfiguration() {
return super.getAuxZPConfiguration() + String.format("AB%2d", currentBank);
}
@Override
public String getName() {
return "Ramworks III";

View File

@ -195,7 +195,7 @@ public class ZipWarpAccelerator extends Device {
@Override
public void tick() {
if (zipUnlockCount > 0) {
if (zipUnlockCount > 0.0) {
zipUnlockCount -= UNLOCK_PENALTY_PER_TICK;
}
}

View File

@ -1,5 +1,6 @@
package jace.lawless;
import jace.LawlessLegends;
import jace.apple2e.Apple2e;
import jace.apple2e.RAM128k;
import jace.apple2e.SoftSwitches;
@ -54,7 +55,7 @@ public class LawlessComputer extends Apple2e {
for (SoftSwitches s : SoftSwitches.values()) {
s.getSwitch().reset();
}
if (showBootAnimation) {
if (showBootAnimation && LawlessLegends.PRODUCTION_MODE) {
(new Thread(this::startAnimation)).start();
} else {
finishColdStart();

View File

@ -4,6 +4,12 @@ import jace.cheat.Cheats;
import jace.core.Computer;
import jace.core.RAMEvent;
import jace.lawless.LawlessVideo.RenderEngine;
import javafx.beans.property.DoubleProperty;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import java.net.URISyntaxException;
import java.net.URL;
/**
* Hacks that affect lawless legends gameplay
@ -11,8 +17,9 @@ import jace.lawless.LawlessVideo.RenderEngine;
public class LawlessHacks extends Cheats {
// Modes specified by the game engine
int MODE_SOFTSWITCH = 0x0C020;
int MODE_SOFTSWITCH_MIN = 0x0C049;
int MODE_SOFTSWITCH_MAX = 0x0C04F;
int SFX_TRIGGER = 0x0C069;
public LawlessHacks(Computer computer) {
super(computer);
@ -27,11 +34,15 @@ public class LawlessHacks extends Cheats {
public void registerListeners() {
// Observe graphics changes
addCheat(RAMEvent.TYPE.ANY, false, (e) -> {
if ((e.getAddress() & 0x0FFF0) == MODE_SOFTSWITCH) {
System.out.println("Trapped " + e.getType().toString() + " to $"+Integer.toHexString(e.getAddress()));
setEngineByOrdinal(e.getAddress() - MODE_SOFTSWITCH);
int addr = e.getAddress();
if (addr >= MODE_SOFTSWITCH_MIN && e.getAddress() <= MODE_SOFTSWITCH_MAX) {
System.out.println("Trapped " + e.getType().toString() + " to $" + Integer.toHexString(e.getAddress()));
setEngineByOrdinal(e.getAddress() - MODE_SOFTSWITCH_MIN);
}
}, MODE_SOFTSWITCH, MODE_SOFTSWITCH | 0x0f);
}, MODE_SOFTSWITCH_MIN, MODE_SOFTSWITCH_MAX);
addCheat(RAMEvent.TYPE.WRITE, false, (e) -> {
playSound(e.getNewValue());
}, SFX_TRIGGER, SFX_TRIGGER);
}
@Override
@ -51,4 +62,150 @@ public class LawlessHacks extends Cheats {
video.setEngine(RenderEngine.UNKNOWN);
}
}
}
int currentSong;
Thread playbackEffect;
MediaPlayer currentSongPlayer;
MediaPlayer currentSfxPlayer;
private void playSound(int soundNumber) {
boolean isMusic = soundNumber >= 0;
int track = soundNumber & 0x07f;
if (track == 0) {
if (isMusic) {
// System.out.println("Stop music");
stopMusic();
} else {
// System.out.println("Stop sfx");
stopSfx();
}
} else if (isMusic) {
// System.out.println("Play music "+track);
playMusic(track);
} else {
// System.out.println("Play sfx "+track);
playSfx(track);
}
}
private Media getAudioTrack(String file) {
String pathStr = "jace/data/sound/" + file;
// System.out.println("looking in "+pathStr);
URL path = getClass().getClassLoader().getResource(pathStr);
if (path == null) {
return null;
}
try {
return new Media(path.toURI().toString());
} catch (URISyntaxException e) {
e.printStackTrace();
return null;
}
}
private void playMusic(int track) {
if (currentSong != track) {
fadeOutSong(() -> startNewSong(track));
} else {
new Thread(() -> startNewSong(track)).start();
}
}
private void stopSongEffect() {
if (playbackEffect != null && playbackEffect.isAlive()) {
playbackEffect.interrupt();
Thread.yield();
}
playbackEffect = null;
}
private void fadeOutSong(Runnable nextAction) {
stopSongEffect();
if (currentSongPlayer != null) {
playbackEffect = new Thread(() -> {
DoubleProperty volume = currentSongPlayer.volumeProperty();
while (playbackEffect == Thread.currentThread() && currentSongPlayer != null && volume.get() > 0.0) {
// System.out.println("Fading down: " + volume.get());
volume.set(volume.get() - FADE_AMT);
try {
Thread.sleep(FADE_SPEED);
} catch (InterruptedException e) {
playbackEffect = null;
return;
}
}
currentSongPlayer.stop();
currentSongPlayer = null;
if (nextAction != null) {
nextAction.run();
}
});
playbackEffect.start();
} else if (nextAction != null) {
new Thread(nextAction).start();
}
}
double FADE_AMT = 0.05; // 5% per interval, or 20 stops between 0% and 100%
int FADE_SPEED = 100; // 100ms per 5%, or 2 second duration
private void startNewSong(int track) {
if (track != currentSong || currentSongPlayer == null) {
// If the same song is already playing don't restart it
Media song = getAudioTrack("BGM-" + track + ".mp3");
if (song == null) {
System.out.println("Unable to start song " + track + "; File not found");
return;
}
currentSongPlayer = new MediaPlayer(song);
currentSongPlayer.setCycleCount(MediaPlayer.INDEFINITE);
currentSongPlayer.setVolume(0.0);
currentSongPlayer.play();
currentSong = track;
}
// But if the same song was already playing but possibly fading out
// then this will fade it back in neatly.
stopSongEffect();
// System.out.println("Starting "+track+" NOW");
playbackEffect = new Thread(() -> {
DoubleProperty volume = currentSongPlayer.volumeProperty();
while (playbackEffect == Thread.currentThread() && currentSongPlayer != null && volume.get() < 1.0) {
volume.set(volume.get() + FADE_AMT);
// System.out.println("Fading up: " + volume.get());
try {
Thread.sleep(FADE_SPEED);
} catch (InterruptedException e) {
playbackEffect = null;
return;
}
}
});
playbackEffect.start();
}
private void stopMusic() {
stopSongEffect();
fadeOutSong(null);
}
private void playSfx(int track) {
new Thread(() -> {
Media sfx = getAudioTrack("SFX-" + track + ".mp3");
if (sfx == null) {
System.out.println("Unable to start SFX " + track + "; File not found");
return;
}
currentSfxPlayer = new MediaPlayer(sfx);
currentSfxPlayer.setCycleCount(1);
currentSfxPlayer.play();
}).start();
}
private void stopSfx() {
if (currentSfxPlayer != null) {
currentSfxPlayer.stop();
currentSfxPlayer = null;
}
}
}

View File

@ -45,7 +45,7 @@ public class LawlessVideo extends VideoNTSC {
39, 130, 78, 191
}),
TITLE(new int[]{
56, 162, 224, 190
16, 162, 64, 190
}),
UNKNOWN;
boolean[][] colorMask;

View File

@ -83,7 +83,7 @@
</Button>
</children>
</TilePane>
<TilePane alignment="TOP_RIGHT" hgap="5.0" vgap="5.0" HBox.hgrow="ALWAYS">
<!-- <TilePane alignment="TOP_RIGHT" hgap="5.0" vgap="5.0" HBox.hgrow="ALWAYS">
<children>
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="IDE">
<graphic>
@ -113,7 +113,7 @@
</graphic>
</Button>
</children>
</TilePane>
</TilePane>-->
</children>
</HBox>
</top>
@ -153,7 +153,7 @@
</TilePane>
<TilePane alignment="TOP_RIGHT" hgap="5.0" vgap="5.0" HBox.hgrow="ALWAYS">
<children>
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Play">
<!-- <Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Play">
<graphic>
<ImageView>
<image>
@ -161,7 +161,7 @@
</image>
</ImageView>
</graphic>
</Button>
</Button>-->
<Button alignment="TOP_LEFT" contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Restart" TilePane.alignment="TOP_RIGHT">
<graphic>
<ImageView>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 724 B

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 914 B

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 959 B

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -32,7 +32,7 @@
}
.uiActionButton ImageView, .uiSpeedSlider ImageView {
-fx-effect: dropshadow(gaussian , rgba(128,255,128,0.75) , 2,1.0,0,0);
-fx-effect: dropshadow(gaussian , rgba(64,255,64,0.25) , 2,1.0,0,0);
}
.uiSpeedSlider AnchorPane {

View File

@ -5,13 +5,13 @@
*/
package jace.ide;
import com.google.common.collect.Lists;
import jace.applesoft.ApplesoftProgram;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
@ -55,14 +55,14 @@ public class ApplesoftTest {
@Test
public void deserializeBinaryTest() {
ApplesoftProgram program = ApplesoftProgram.fromBinary(Lists.newArrayList(lemonadeStandBinary), 0x0801);
ApplesoftProgram program = ApplesoftProgram.fromBinary(Arrays.asList(lemonadeStandBinary), 0x0801);
assertNotNull(program);
assertNotSame("", program.toString());
}
@Test
public void roundTripStringComparisonTest() {
ApplesoftProgram program = ApplesoftProgram.fromBinary(Lists.newArrayList(lemonadeStandBinary), 0x0801);
ApplesoftProgram program = ApplesoftProgram.fromBinary(Arrays.asList(lemonadeStandBinary), 0x0801);
String serialized = program.toString();
ApplesoftProgram deserialized = ApplesoftProgram.fromString(serialized);
String[] serializedLines = serialized.split("\\n");