lawless-legends/Platform/Apple/tools/jace/src/test/java/jace/cpu/CycleCountTest.java

149 lines
5.1 KiB
Java

/*
* Copyright 2023 org.badvision.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jace.cpu;
import jace.Emulator;
import jace.core.Computer;
import jace.core.Utility;
import jace.core.SoundMixer;
import jace.apple2e.MOS65C02;
import jace.apple2e.RAM128k;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
import static jace.TestUtils.*;
/**
* More advanced cycle counting tests. These help ensure CPU runs correctly so things
* like vapor lock and speaker sound work as expected.
* @author brobert
*/
public class CycleCountTest {
static Computer computer;
static MOS65C02 cpu;
static RAM128k ram;
@BeforeClass
public static void setupClass() {
Utility.setHeadlessMode(true);
SoundMixer.MUTE = true;
computer = Emulator.getComputer();
cpu = (MOS65C02) computer.getCpu();
ram = (RAM128k) computer.getMemory();
// Let the final stopping point for tests loop indefinitely
// This also lets us figure out how long it's been looping when the test ends
assemble("jmp $1000", 0x01000);
}
private String BELL = """
SPKR = $C030
BELL ldy #$01
BELL2 lda #$0c
jsr WAIT
lda SPKR
dey
bne BELL2
jmp $1000 ; Success case is jumping to $1000
WAIT sec
WAIT2 pha
WAIT3 sbc #$01
bne WAIT3
pla
sbc #$01
bne WAIT2
rts
""";
/**
* Test that the wait routine for beep takes ~552 cycles
* Calling WAIT with A=#$c (12) should take 535 cycles
* according to the tech ref notes: =1/2*(26+27*A+5*A^2) where A = 12 (0x0c)
* The BELL routine has an additional 17 cycles per iteration
*
* This test manually triggers the CPU to run directly with no other emulation occurring.
*/
@Test
public void testDirectBeeperCycleCount() {
AtomicInteger breakpointEncountered = new AtomicInteger();
AtomicInteger cycleCount = new AtomicInteger();
// This listener will increment our breakpoint counter if it reaches our desired stoppoing point in time
ram.addExecutionTrap(0x01000, e -> breakpointEncountered.incrementAndGet());
// This faux device counts the number of cycles executed
cpu.addChildDevice(createSimpleDevice(()->{
if (breakpointEncountered.get() == 0) {
cycleCount.incrementAndGet();
System.out.print("*");
}
}, "Cycle Counter"));
// Now run the CPU and see if we got the expected results
cpu.resume();
runAssemblyCode(BELL, 552);
assertEquals("Should have encountered the breakpoint", 1, breakpointEncountered.get());
assertEquals("Should have taken about 551 cycles to complete", 551, cycleCount.get());
cpu.suspend();
}
/**
* This is the same test as before except steps are executed from the motherboard
*/
@Test
public void testMachineBeeperCycleCount() {
AtomicInteger breakpointEncountered = new AtomicInteger();
AtomicInteger cycleCount = new AtomicInteger();
// This listener will increment our breakpoint counter if it reaches our desired stoppoing point in time
ram.addExecutionTrap(0x01000, e -> breakpointEncountered.incrementAndGet());
// This faux device counts the number of cycles executed
cpu.addChildDevice(createSimpleDevice(()->{
if (breakpointEncountered.get() == 0) {
cycleCount.incrementAndGet();
System.out.print("*");
}
}, "Cycle Counter"));
// This assembles the code and sets PC but doesn't actually do anything
assemble(BELL, 0x0300);
Emulator.getComputer().getMotherboard().resumeInThread();
for (int i=0; i < 552; i++) {
Emulator.getComputer().getMotherboard().doTick();
}
Emulator.getComputer().getMotherboard().suspend();
assertEquals("Should have encountered the breakpoint", 1, breakpointEncountered.get());
assertEquals("Should have taken about 551 cycles to complete", 551, cycleCount.get());
}
// The CPU cycle count should work the same even when the emulator is sped up.
@Test
public void testAcceleratedCycleCount() {
Emulator.getComputer().getMotherboard().setMaxSpeed(true);
Emulator.getComputer().getMotherboard().setSpeedInPercentage(20000);
testMachineBeeperCycleCount();
Emulator.getComputer().getMotherboard().setMaxSpeed(false);
Emulator.getComputer().getMotherboard().setSpeedInPercentage(100);
}
}