diff --git a/pom.xml b/pom.xml index 8bd1212..70065b4 100644 --- a/pom.xml +++ b/pom.xml @@ -123,7 +123,7 @@ - + diff --git a/src/main/java/jace/Emulator.java b/src/main/java/jace/Emulator.java index ac4d222..63ed6d8 100644 --- a/src/main/java/jace/Emulator.java +++ b/src/main/java/jace/Emulator.java @@ -61,6 +61,10 @@ public class Emulator { if (instance.computer != null) { instance.computer.getMotherboard().suspend(); instance.computer.getMotherboard().detach(); + if (instance.computer.getVideo() != null) { + instance.computer.getVideo().suspend(); + instance.computer.getVideo().detach(); + } } } instance = null; diff --git a/src/main/java/jace/applesoft/ApplesoftProgram.java b/src/main/java/jace/applesoft/ApplesoftProgram.java index 6e42f2b..3178477 100755 --- a/src/main/java/jace/applesoft/ApplesoftProgram.java +++ b/src/main/java/jace/applesoft/ApplesoftProgram.java @@ -211,7 +211,7 @@ public class ApplesoftProgram { * Move variables around to accommodate bigger program * @param programEnd Program ending address */ - private void relocateVariables(int programEnd) { + public void relocateVariables(int programEnd) { Emulator.withMemory(memory->{ int currentEnd = memory.readWordRaw(END_OF_PROG_POINTER); memory.writeWord(END_OF_PROG_POINTER, programEnd, false, true); diff --git a/src/main/java/jace/core/TimedDevice.java b/src/main/java/jace/core/TimedDevice.java index d5ad092..00f3d98 100644 --- a/src/main/java/jace/core/TimedDevice.java +++ b/src/main/java/jace/core/TimedDevice.java @@ -81,6 +81,7 @@ public abstract class TimedDevice extends Device { public final void resetSyncTimer() { nextSync = System.nanoTime() + nanosPerInterval; + waitUntil = null; cycleTimer = 0; } @@ -119,9 +120,7 @@ public abstract class TimedDevice extends Device { public final void setMaxSpeed(boolean enabled) { maxspeed = enabled; - if (!enabled) { - resetSyncTimer(); - } + resetSyncTimer(); } public final boolean isMaxSpeedEnabled() { @@ -129,7 +128,7 @@ public abstract class TimedDevice extends Device { } public final boolean isMaxSpeed() { - return forceMaxspeed || maxspeed; + return forceMaxspeed || maxspeed || tempSpeedDuration > 0; } public final long getSpeedInHz() { diff --git a/src/test/java/jace/AbstractFXTest.java b/src/test/java/jace/AbstractFXTest.java index b0581c7..062d2ef 100644 --- a/src/test/java/jace/AbstractFXTest.java +++ b/src/test/java/jace/AbstractFXTest.java @@ -1,5 +1,6 @@ package jace; +import org.junit.AfterClass; import org.junit.BeforeClass; import javafx.application.Platform; @@ -12,5 +13,11 @@ public abstract class AbstractFXTest { fxInitialized = true; Platform.startup(() -> {}); } - } + } + + @AfterClass + public static void shutdown() { + Emulator.abort(); + Platform.exit(); + } } diff --git a/src/test/java/jace/applesoft/ApplesoftTest.java b/src/test/java/jace/applesoft/ApplesoftTest.java index b7f0899..4538972 100644 --- a/src/test/java/jace/applesoft/ApplesoftTest.java +++ b/src/test/java/jace/applesoft/ApplesoftTest.java @@ -2,18 +2,63 @@ package jace.applesoft; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import java.util.Collections; + +import org.junit.BeforeClass; import org.junit.Test; +import jace.Emulator; +import jace.TestUtils; +import jace.apple2e.MOS65C02; +import jace.apple2e.RAM128k; +import jace.core.Computer; +import jace.core.SoundMixer; +import jace.ide.Program; +import jace.ide.Program.DocumentType; + public class ApplesoftTest { + static Computer computer; + public static MOS65C02 cpu; + static RAM128k ram; + + @BeforeClass + public static void setupClass() { + TestUtils.initComputer(); + SoundMixer.MUTE = true; + computer = Emulator.withComputer(c->c, null); + cpu = (MOS65C02) computer.getCpu(); + ram = (RAM128k) computer.getMemory(); + } + @Test public void fromStringTest() { - String programSource = "10 PRINT \"Hello, World!\"\n20 PRINT \"Goodbye!\""; - ApplesoftProgram program = ApplesoftProgram.fromString(programSource); - assertNotNull(program); - assertEquals(2, program.lines.size()); - Line line1 = program.lines.get(0); + String programSource = "10 PRINT \"Hello, World!\"\n\n20 PRINT \"Goodbye!\"\n"; + ApplesoftHandler handler = new ApplesoftHandler(); + // We want to test as much as we can but right now it's heavily integrated with the UI + Program program = new Program(DocumentType.applesoft, Collections.emptyMap()) { + String value; + @Override + public String getValue() { + return value; + } + @Override + public void setValue(String value) { + this.value = value; + } + }; + program.setValue(programSource); + var compileResult = handler.compile(program); + assertNotNull(compileResult.getCompiledAsset()); + assertTrue(compileResult.isSuccessful()); + assertTrue(compileResult.getErrors().isEmpty()); + assertTrue(compileResult.getWarnings().isEmpty()); + assertTrue(compileResult.getOtherMessages().isEmpty()); + assertTrue(compileResult.getRawOutput().isEmpty()); + assertEquals(2, compileResult.getCompiledAsset().lines.size()); + Line line1 = compileResult.getCompiledAsset().lines.get(0); assertEquals(10, line1.getNumber()); assertEquals(1, line1.getCommands().size()); Command command1 = line1.getCommands().get(0); @@ -23,6 +68,24 @@ public class ApplesoftTest { match += command1.parts.get(idx).toString(); } assertEquals("\"Hello, World!\"", match); + // Does nothing but test coverage is test coverage + handler.clean(null); + + // Now let's try to execute and see if we can read the program back + handler.execute(compileResult); + + ApplesoftProgram program2 = Emulator.withComputer(c->ApplesoftProgram.fromMemory(c.getMemory()), null); + assertEquals(2, program2.getLength()); + Line line2 = program2.lines.get(0); + assertEquals(10, line2.getNumber()); + assertEquals(1, line2.getCommands().size()); + Command command2 = line2.getCommands().get(0); + assertEquals(0xBA, command2.parts.get(0).getByte() & 0x0ff); + match = ""; + for (int idx=1; idx < command2.parts.size(); idx++) { + match += command2.parts.get(idx).toString(); + } + assertEquals("\"Hello, World!\"", match); } @Test @@ -35,4 +98,15 @@ public class ApplesoftTest { String programSource = program.toString(); assertEquals("10 PRINT \"Hello, world!\"\n20 PRINT \"Goodbye!\"\n", programSource); } + + @Test + public void relocateVariablesTest() { + ApplesoftProgram program = new ApplesoftProgram(); + Line line1 = Line.fromString("10 print \"Hello, world!\""); + Line line2 = Line.fromString("20 print \"Goodbye!\""); + program.lines.add(line1); + program.lines.add(line2); + program.relocateVariables(0x6000); + // We need better assertions here but for now we just want to make sure it doesn't crash + } } \ No newline at end of file diff --git a/src/test/java/jace/core/TimedDeviceTest.java b/src/test/java/jace/core/TimedDeviceTest.java new file mode 100644 index 0000000..714fb89 --- /dev/null +++ b/src/test/java/jace/core/TimedDeviceTest.java @@ -0,0 +1,102 @@ +package jace.core; + +import org.junit.Before; +import org.junit.Test; + +import jace.AbstractFXTest; + +import static org.junit.Assert.*; + +public class TimedDeviceTest extends AbstractFXTest { + + private TimedDevice timedDevice; + public int countedTicks = 0; + + @Before + public void setUp() { + countedTicks = 0; + timedDevice = new TimedDevice(true) { + + @Override + public String getShortName() { + return "Test"; + } + + @Override + protected String getDeviceName() { + return "Test"; + } + + @Override + public void tick() { + countedTicks++; + } + }; + } + + @Test + public void testSetSpeedInHz() { + long newSpeed = 2000000; + timedDevice.setSpeedInHz(newSpeed); + assertEquals(newSpeed, timedDevice.getSpeedInHz()); + } + + @Test + public void testSetSpeedInPercentage() { + int ratio = 50; + timedDevice.setSpeedInPercentage(ratio); + assertEquals(ratio, timedDevice.getSpeedRatio()); + } + + @Test + public void testMaxSpeed() { + // Use temp max speed + timedDevice.setMaxSpeed(false); + timedDevice.enableTempMaxSpeed(); + assertTrue("Max speed enabled", timedDevice.isMaxSpeed()); + timedDevice.disableTempMaxSpeed(); + assertFalse("Max speed disabled", timedDevice.isMaxSpeed()); + // Run 250 cycles and make sure none were skipped + timedDevice.setSpeedInHz(1000); + timedDevice.resume(); + for (int i=0 ; i<250 ; i++) { + timedDevice.enableTempMaxSpeed(); + timedDevice.doTick(); + } + assertEquals("250 ticks were counted", 250, countedTicks); + // Disable temp max speed + timedDevice.disableTempMaxSpeed(); + countedTicks = 0; + for (int i=0 ; i<250 ; i++) { + timedDevice.doTick(); + } + assertTrue("Should have counted fewer than 250 ticks", countedTicks < 250); + // Now use max speed + timedDevice.setMaxSpeed(true); + countedTicks = 0; + for (int i=0 ; i<250 ; i++) { + timedDevice.doTick(); + } + assertEquals("250 ticks were counted", 250, countedTicks); + } + + @Test + public void testReconfigure() { + timedDevice.reconfigure(); + // Add assertions here + } + + @Test + public void testTicks() { + timedDevice.setSpeedInHz(1000); + timedDevice.resume(); + long now = System.nanoTime(); + for (countedTicks=0 ; countedTicks<250 ; ) { + timedDevice.doTick(); + } + assertEquals("250 ticks were counted", 250, countedTicks); + long ellapsed = System.nanoTime() - now; + assertTrue("About 250ms elapsed", ellapsed / 1000000 >= 240); + } + +} \ No newline at end of file