Basic speedup hacks and B&W rendering for text

This commit is contained in:
Brendan Robert 2017-12-28 18:48:35 -06:00
parent cfb90d27a1
commit 05e59a5454
9 changed files with 229 additions and 12 deletions

View File

@ -12,7 +12,7 @@
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
</goals>
<properties>
<exec.args>-classpath %classpath jace.JaceApplication</exec.args>
<exec.args>-classpath %classpath jace.LawlessLegends</exec.args>
<exec.executable>java</exec.executable>
</properties>
</action>
@ -26,7 +26,7 @@
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
</goals>
<properties>
<exec.args>-classpath %classpath jace.JaceApplication</exec.args>
<exec.args>-classpath %classpath jace.LawlessLegends</exec.args>
<exec.executable>java</exec.executable>
<jpda.listen>true</jpda.listen>
</properties>
@ -41,7 +41,7 @@
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
</goals>
<properties>
<exec.args>-Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath jace.JaceApplication</exec.args>
<exec.args>-agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath jace.LawlessLegends</exec.args>
<exec.executable>java</exec.executable>
<jpda.listen>true</jpda.listen>
</properties>

View File

@ -5,7 +5,7 @@
*/
package jace;
import com.google.common.io.Files;
import jace.config.Configuration;
import jace.core.RAM;
import jace.core.RAMEvent;
import jace.core.RAMListener;
@ -14,9 +14,10 @@ import jace.hardware.CardDiskII;
import jace.hardware.CardMockingboard;
import jace.hardware.CardRamFactor;
import jace.hardware.CardRamworks;
import jace.hardware.CardThunderclock;
import jace.hardware.PassportMidiInterface;
import jace.hardware.massStorage.CardMassStorage;
import jace.lawless.LawlessHacks;
import jace.lawless.LawlessVideo;
import jace.library.DiskType;
import jace.library.MediaEntry;
import jace.library.MediaEntry.MediaFile;
@ -24,7 +25,6 @@ import jace.ui.MetacheatUI;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.StandardCopyOption;
@ -66,7 +66,7 @@ public class LawlessLegends extends Application {
primaryStage.setScene(s);
primaryStage.setTitle("Lawless Legends");
EmulatorUILogic.scaleIntegerRatio();
Utility.loadIcon("revolver_icon.png").ifPresent(primaryStage.getIcons()::add);
Utility.loadIcon("game_icon.png").ifPresent(primaryStage.getIcons()::add);
} catch (IOException exception) {
throw new RuntimeException(exception);
}
@ -153,11 +153,14 @@ public class LawlessLegends extends Application {
Emulator.computer.joy2enabled = false;
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(CardMockingboard.class);
Emulator.computer.card2.setValue(PassportMidiInterface.class);
Emulator.computer.cheatEngine.setValue(LawlessHacks.class);
Configuration.buildTree();
Emulator.computer.reconfigure();
RAM memory = Emulator.computer.memory;

View File

@ -57,14 +57,14 @@ public class VideoNTSC extends VideoDHGR {
public boolean enableVideo7 = true;
// Scanline represents 560 bits, divided up into 28-bit words
int[] scanline = new int[20];
static int[] divBy28 = new int[560];
static public int[] divBy28 = new int[560];
static {
for (int i = 0; i < 560; i++) {
divBy28[i] = i / 28;
}
}
boolean[] colorActive = new boolean[80];
protected boolean[] colorActive = new boolean[80];
int rowStart = 0;
public VideoNTSC(Computer computer) {

View File

@ -97,7 +97,7 @@ public abstract class Cheats extends Device {
super.detach();
}
abstract void registerListeners();
public abstract void registerListeners();
protected void unregisterListeners() {
listeners.stream().forEach((l) -> {

View File

@ -134,7 +134,7 @@ public class MetaCheat extends Cheats {
}
@Override
void registerListeners() {
public void registerListeners() {
}
public void addCheat(DynamicCheat cheat) {

View File

@ -70,7 +70,7 @@ public class MontezumasRevengeCheats extends Cheats {
};
@Override
void registerListeners() {
public void registerListeners() {
RAM memory = Emulator.computer.memory;
if (repulsiveHack) {
addCheat(RAMEvent.TYPE.WRITE, this::repulsiveBehavior, 0x1508, 0x1518);

View File

@ -0,0 +1,98 @@
package jace.lawless;
import jace.apple2e.MOS65C02;
import jace.cheat.Cheats;
import jace.core.Computer;
import jace.core.RAMEvent;
import jace.core.Utility;
import jace.lawless.LawlessVideo.RenderEngine;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Hacks that affect lawless legends gameplay
*/
public class LawlessHacks extends Cheats {
Computer computer;
// Location of font routines
int FONT_ROUTINES = 0x0EC00;
int FONT_SPEEDUP_CYCLES = 10000;
int FONT_ROUTINES_LEN = 0x0f00;
int ENGINE_ADDR = 0x06000;
int ENGINE_FIRST_OPCODE = ENGINE_ADDR + (13 * 3);
int DETECT_ENGINE_WRITE = 0x060FF;
Cheats speedupRequester = this;
AtomicInteger speedupCounter = new AtomicInteger();
public LawlessHacks(Computer computer) {
super(computer);
this.computer = computer;
}
@Override
public void registerListeners() {
for (int entry = 0; entry < 13; entry++) {
int targetAddress = FONT_ROUTINES + (entry * 3);
addCheat(RAMEvent.TYPE.EXECUTE, (e) -> {
if (e.getAddress() == targetAddress) {
computer.motherboard.requestSpeed(speedupRequester);
speedupCounter.set(FONT_SPEEDUP_CYCLES);
}
}, targetAddress);
}
addCheat(RAMEvent.TYPE.WRITE, false, (e) -> {
if (e.getAddress() == DETECT_ENGINE_WRITE) {
detectActiveEngine();
}
}, DETECT_ENGINE_WRITE);
}
@Override
public String getDeviceName() {
return "Lawless Legends optimizations";
}
@Override
public void tick() {
if (speedupCounter.get() > 0 && speedupCounter.decrementAndGet() <= 0) {
int pc = computer.getCpu().getProgramCounter();
if (pc >= FONT_ROUTINES && pc <= FONT_ROUTINES + FONT_ROUTINES_LEN) {
speedupCounter.addAndGet(500);
computer.motherboard.requestSpeed(speedupRequester);
} else {
computer.motherboard.cancelSpeedRequest(speedupRequester);
}
}
}
private void detectActiveEngine() {
// for (int i = 0x06000; i < 0x06080;) {
// System.out.printf("%04x: ", i);
// for (int j = 0; j < 16; j++, i++) {
// System.out.printf("%02x ", computer.getMemory().readRaw(i) & 0x0ff);
// }
// System.out.println();
// }
int firstPageByte = computer.getMemory().readRaw(ENGINE_ADDR) & 0x0ff;
int firstDataByte = computer.getMemory().readRaw(ENGINE_FIRST_OPCODE) & 0x0ff;
int secondDataByte = computer.getMemory().readRaw(ENGINE_FIRST_OPCODE + 1) & 0x0ff;
if (firstPageByte == MOS65C02.OPCODE.JMP_AB.getCode()
&& firstDataByte == MOS65C02.OPCODE.LDX_ZP.getCode()) {
// 2D Engine: First instruction is LDX MAP_PARTITION
LawlessVideo.setEngine(RenderEngine._2D);
} else if (firstPageByte == MOS65C02.OPCODE.JMP_AB.getCode()
&& firstDataByte == 0
&& secondDataByte == 0) {
// 3D Engine: First byte is a zero for MapHeader
LawlessVideo.setEngine(RenderEngine._3D);
} else if (firstPageByte == MOS65C02.OPCODE.JMP_AB.getCode()
&& firstDataByte == 0
&& secondDataByte == 0x067) {
// 3D Engine: First byte is a zero for MapHeader
LawlessVideo.setEngine(RenderEngine.PORTRAIT);
} else {
LawlessVideo.setEngine(RenderEngine.UNKNOWN);
}
}
}

View File

@ -0,0 +1,116 @@
package jace.lawless;
import jace.apple2e.RAM128k;
import jace.apple2e.VideoNTSC;
import jace.core.Computer;
import jace.core.PagedMemory;
import java.util.Arrays;
import javafx.scene.image.WritableImage;
/**
* Lawless-enhanced video output for readable text
*/
public class LawlessVideo extends VideoNTSC {
private static RenderEngine activeEngine = RenderEngine.UNKNOWN;
private boolean invActive = false;
private boolean titleScreen = false;
public static enum RenderEngine {
_2D(new int[]{
9, 8, 34, 17,
44, 24, 76, 136,
44, 143, 76, 184
}),
_3D(new int[]{
9, 8, 34, 17,
44, 24, 76, 136,
44, 143, 76, 184,
8, 172, 14, 182,}),
INVENTORY(new int[]{
2, 6, 78, 186
}),
PORTRAIT, UNKNOWN;
boolean[][] colorMask;
RenderEngine(int[] mask) {
this();
for (int i = 0; i < mask.length; i += 4) {
int x1 = mask[i],
y1 = mask[i + 1],
x2 = mask[i + 2],
y2 = mask[i + 3];
for (int y = y1; y < y2; y++) {
for (int x = x1; x < x2; x++) {
colorMask[y][x] = false;
}
}
}
}
RenderEngine() {
colorMask = new boolean[192][80];
for (int y = 0; y < 192; y++) {
colorMask[y] = new boolean[80];
Arrays.fill(colorMask[y], true);
}
}
};
public LawlessVideo(Computer computer) {
super(computer);
}
public static void setEngine(RenderEngine e) {
activeEngine = e;
// System.out.println("Detected engine: " + e.name());
}
static public int[] divBy56 = new int[560];
static {
for (int i = 0; i < 560; i++) {
divBy56[i] = i / 56;
}
}
@Override
public void vblankStart() {
super.vblankStart();
// Row 5 = Black
int row4 = getSummary(4);
int row5 = getSummary(5);
int row6 = getSummary(6);
int row7 = getSummary(7);
// Rows 6,7 = White
invActive = row5 == 0
&& row6 == 1270
&& row7 == 1270;
titleScreen = row4 == 828 && row5 == 513 && row6 == 382;
}
public int getSummary(int row) {
PagedMemory mainMemory = ((RAM128k) computer.getMemory()).getMainMemory();
int rowAddr = getCurrentWriter().getYOffset(row);
int total = 0;
for (int i = rowAddr + 4; i < rowAddr + 14; i++) {
total += mainMemory.readByte(i) & 0x07f;
}
return total;
}
@Override
public void hblankStart(WritableImage screen, int y, boolean isDirty) {
int rowStart = getCurrentWriter().getYOffset(y);
if (rowStart >= 0x02000 && !titleScreen) {
boolean[] color = activeEngine.colorMask[y];
if (invActive) {
color = RenderEngine.INVENTORY.colorMask[y];
} else if (activeEngine == RenderEngine.PORTRAIT) {
color = RenderEngine._2D.colorMask[y];
}
System.arraycopy(color, 0, colorActive, 0, 80);
}
super.hblankStart(screen, y, isDirty); //To change body of generated methods, choose Tools | Templates.
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB