206 lines
7.1 KiB
Java
206 lines
7.1 KiB
Java
package jace.lawless;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.concurrent.Semaphore;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
|
|
import jace.apple2e.Apple2e;
|
|
import jace.apple2e.RAM128k;
|
|
import jace.apple2e.SoftSwitches;
|
|
import jace.apple2e.VideoNTSC;
|
|
import jace.config.ConfigurableField;
|
|
import jace.core.Video;
|
|
import jace.library.MediaConsumer;
|
|
|
|
/**
|
|
* Extends standard implementation to provide different cold start behavior
|
|
*/
|
|
public class LawlessComputer extends Apple2e {
|
|
|
|
byte[] bootScreen = null;
|
|
boolean performedBootAnimation = false;
|
|
LawlessImageTool gameDiskHandler = new LawlessImageTool();
|
|
@ConfigurableField(name = "Boot Animation")
|
|
public boolean showBootAnimation = true;
|
|
|
|
public LawlessComputer() {
|
|
super();
|
|
}
|
|
|
|
public void initLawlessLegendsConfiguration() {
|
|
reconfigure(); // Required before anything so that memory is initialized
|
|
this.cheatEngine.setValue(LawlessHacks.class);
|
|
this.activeCheatEngine = new LawlessHacks(this);
|
|
this.activeCheatEngine.attach();
|
|
blankTextPage1();
|
|
reconfigure();
|
|
}
|
|
|
|
private void blankTextPage1() {
|
|
// Fill text page 1 with spaces
|
|
for (int i = 0x0400; i < 0x07FF; i++) {
|
|
getMemory().write(i, (byte) (0x080 | ' '), false, false);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void coldStart() {
|
|
motherboard.whileSuspended(()->{
|
|
RAM128k ram = (RAM128k) getMemory();
|
|
ram.zeroAllRam();
|
|
blankTextPage1();
|
|
for (SoftSwitches s : SoftSwitches.values()) {
|
|
s.getSwitch().reset();
|
|
}
|
|
});
|
|
if (showBootAnimation && PRODUCTION_MODE) {
|
|
(new Thread(this::startAnimation)).start();
|
|
} else {
|
|
finishColdStart();
|
|
}
|
|
}
|
|
|
|
public void startAnimation() {
|
|
cpu.setPaused(true);
|
|
for (SoftSwitches s : SoftSwitches.values()) {
|
|
s.getSwitch().reset();
|
|
}
|
|
SoftSwitches._80COL.getSwitch().setState(true);
|
|
SoftSwitches.TEXT.getSwitch().setState(false);
|
|
SoftSwitches.HIRES.getSwitch().setState(true);
|
|
SoftSwitches.PAGE2.getSwitch().setState(false);
|
|
SoftSwitches.DHIRES.getSwitch().setState(true);
|
|
((VideoNTSC) getVideo()).enableVideo7 = false;
|
|
getMemory().configureActiveMemory();
|
|
getVideo().configureVideoMode();
|
|
doResume();
|
|
|
|
if (!performedBootAnimation) {
|
|
try {
|
|
performedBootAnimation = true;
|
|
waitForVBL();
|
|
renderWithMask(0, 0, 0, 0);
|
|
renderWithMask(0x0, 0x10, 0, 0x40, 0, 0x01, 0, 0x4);
|
|
renderWithMask(0x8, 0x10, 0x02, 0x40, 0, 0x01, 0x20, 0x4);
|
|
renderWithMask(0x8, 0x11, 0x22, 0x44);
|
|
renderWithMask(0x8, 0x19, 0x22, 0x66, 0x4c, 0x11, 0x33, 044);
|
|
renderWithMask(0x4c, 0x19, 0x33, 0x66);
|
|
renderWithMask(0x4c, 0x5d, 0x33, 0x77, 0x6e, 0x19, 0x3B, 0x66);
|
|
renderWithMask(0x6e, 0x5d, 0x3B, 0x77);
|
|
renderWithMask(0x6e, 0x7f, 0x3b, 0x7f, 0x7f, 0x5d, 0x7f, 0x77);
|
|
renderWithMask(0x7f, 0x7f, 0x7f, 0x7f);
|
|
waitForVBL(230);
|
|
renderWithMask(0x7f, 0x6e, 0x7f, 0x3b, 0x77, 0x6e, 0x5d, 0x3b);
|
|
renderWithMask(0x77, 0x6e, 0x5d, 0x3b);
|
|
renderWithMask(0x77, 0x66, 0x5d, 0x19, 0x33, 0x6e, 0x4c, 0x3b);
|
|
renderWithMask(0x33, 0x66, 0x4c, 0x19);
|
|
renderWithMask(0x33, 0x22, 0x4c, 0x8, 0x11, 0x66, 0x44, 0x19);
|
|
renderWithMask(0x11, 0x22, 0x44, 0x8);
|
|
renderWithMask(0x11, 0, 0x44, 0, 0, 0x22, 0, 0x8);
|
|
renderWithMask(0, 0, 0, 0);
|
|
} catch (InterruptedException ex) {
|
|
Logger.getLogger(LawlessComputer.class.getName()).log(Level.SEVERE, null, ex);
|
|
}
|
|
}
|
|
cpu.setPaused(false);
|
|
finishColdStart();
|
|
|
|
}
|
|
|
|
private void renderWithMask(int... mask) throws InterruptedException {
|
|
RAM128k ram = (RAM128k) getMemory();
|
|
byte[] framebuffer = getBootScreen();
|
|
int maskOffset = 0;
|
|
for (int i = 0; i < 0x02000; i += 2) {
|
|
int next = (framebuffer[i] & 1) << 6;
|
|
Byte b1 = (byte) ((framebuffer[i + 0x02000] & 0x07f) >> 1 | next);
|
|
ram.getAuxMemory().writeByte(0x02000 + i, (byte) (b1 & mask[maskOffset] | 0x080));
|
|
|
|
if (i < 0x01FFF) {
|
|
next = (framebuffer[i + 0x02001] & 1) << 6;
|
|
Byte b2 = (byte) ((framebuffer[i] & 0x07f) >> 1 | next);
|
|
ram.getMainMemory().writeByte(0x02000 + i, (byte) (b2 & mask[maskOffset + 1] | 0x080));
|
|
|
|
next = (framebuffer[i + 1] & 1) << 6;
|
|
Byte b3 = (byte) ((framebuffer[i + 0x02001] & 0x07f) >> 1 | next);
|
|
ram.getAuxMemory().writeByte(0x02001 + i, (byte) (b3 & mask[maskOffset + 2] | 0x080));
|
|
}
|
|
|
|
if (i < 0x01FFE) {
|
|
next = (framebuffer[i + 0x02002] & 1) << 6;
|
|
Byte b4 = (byte) ((framebuffer[i + 1] & 0x07f) >> 1 | next);
|
|
ram.getMainMemory().writeByte(0x02001 + i, (byte) (b4 & mask[maskOffset + 3] | 0x080));
|
|
}
|
|
if (i % 20 != 0) {
|
|
maskOffset = (maskOffset + 4) % mask.length;
|
|
}
|
|
}
|
|
Video.forceRefresh();
|
|
waitForVBL(5);
|
|
}
|
|
|
|
List<Runnable> vblCallbacks = Collections.synchronizedList(new ArrayList<>());
|
|
|
|
public void waitForVBL() throws InterruptedException {
|
|
waitForVBL(0);
|
|
}
|
|
|
|
public void waitForVBL(int count) throws InterruptedException {
|
|
Semaphore s = new Semaphore(0);
|
|
onNextVBL(s::release);
|
|
s.acquire();
|
|
if (count > 1) {
|
|
waitForVBL(count - 1);
|
|
}
|
|
}
|
|
|
|
public void onNextVBL(Runnable r) {
|
|
vblCallbacks.add(r);
|
|
}
|
|
|
|
@Override
|
|
public void notifyVBLStateChanged(boolean state) {
|
|
super.notifyVBLStateChanged(state);
|
|
if (state) {
|
|
while (vblCallbacks != null && !vblCallbacks.isEmpty()) {
|
|
vblCallbacks.remove(0).run();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void finishColdStart() {
|
|
try {
|
|
waitForVBL();
|
|
reboot();
|
|
} catch (InterruptedException ex) {
|
|
Logger.getLogger(LawlessComputer.class.getName()).log(Level.SEVERE, null, ex);
|
|
}
|
|
}
|
|
|
|
private byte[] getBootScreen() {
|
|
if (bootScreen == null) {
|
|
InputStream in = getClass().getResourceAsStream("/jace/data/bootscreen.bin");
|
|
bootScreen = new byte[0x04000];
|
|
int len, offset = 0;
|
|
try {
|
|
while (offset < 0x04000 && (len = in.read(bootScreen, offset, 0x04000 - offset)) > 0) {
|
|
offset += len;
|
|
}
|
|
} catch (IOException ex) {
|
|
Logger.getLogger(LawlessComputer.class.getName()).log(Level.SEVERE, null, ex);
|
|
}
|
|
|
|
}
|
|
return bootScreen;
|
|
}
|
|
|
|
public MediaConsumer getUpgradeHandler() {
|
|
return gameDiskHandler;
|
|
}
|
|
}
|