Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#314] Use AccurateFrequencyRunner #354

Merged
merged 1 commit into from
Dec 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
*/
package net.emustudio.plugins.cpu.intel8080;

import net.emustudio.emulib.plugins.cpu.AccurateFrequencyRunner;
import net.emustudio.emulib.plugins.cpu.CPU;
import net.emustudio.emulib.plugins.memory.MemoryContext;
import net.emustudio.emulib.runtime.helpers.SleepUtils;
import net.emustudio.plugins.cpu.intel8080.api.CpuEngine;
import net.emustudio.plugins.cpu.intel8080.api.DispatchListener;
import org.slf4j.Logger;
Expand All @@ -29,7 +29,6 @@
import java.lang.invoke.MethodHandle;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static net.emustudio.plugins.cpu.intel8080.DispatchTables.DISPATCH_TABLE;

Expand All @@ -55,6 +54,8 @@ public class EmulatorEngine implements CpuEngine {
);
private final MemoryContext<Byte> memory;
private final Context8080Impl context;
private final AccurateFrequencyRunner preciseRunner = new AccurateFrequencyRunner();

public boolean INTE = false; // enabling / disabling of interrupts
public int PC = 0; // program counter
public int SP = 0; // stack pointer
Expand Down Expand Up @@ -100,52 +101,29 @@ public CPU.RunState step() throws Exception {
return currentRunState;
}

@SuppressWarnings("BusyWait")
public CPU.RunState run(CPU cpu) {
final long slotNanos = SleepUtils.SLEEP_PRECISION;
final double slotMicros = slotNanos / 1000.0;
final int cyclesPerSlot = (int) (slotMicros * context.getCPUFrequency() / 1000.0); // frequency in kHZ -> MHz

currentRunState = CPU.RunState.STATE_RUNNING;
long delayNanos = SleepUtils.SLEEP_PRECISION;

long startTime = System.nanoTime();
long executedCyclesPerSlot = 0;
while (!Thread.currentThread().isInterrupted() && (currentRunState == CPU.RunState.STATE_RUNNING)) {
try {
if (delayNanos > 0) {
Thread.sleep(TimeUnit.NANOSECONDS.toMillis(delayNanos));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}

long endTime = System.nanoTime();
long targetCycles = (endTime - startTime) / slotNanos * cyclesPerSlot;

while ((executedCyclesPerSlot < targetCycles) && !Thread.currentThread().isInterrupted() && (currentRunState == CPU.RunState.STATE_RUNNING)) {
try {
if (cpu.isBreakpointSet(PC)) {
throw new Breakpoint();
return preciseRunner.run(
() -> (double) context.getCPUFrequency(),
() -> {
try {
if (cpu.isBreakpointSet(PC)) {
currentRunState = CPU.RunState.STATE_STOPPED_BREAK;
} else {
int cycles = dispatch();
preciseRunner.addExecutedCycles(cycles);
context.passedCycles(cycles);
}
} catch (IndexOutOfBoundsException e) {
LOGGER.error("Unexpected error", e);
currentRunState = CPU.RunState.STATE_STOPPED_ADDR_FALLOUT;
} catch (Throwable e) {
LOGGER.error("Unexpected error", e);
currentRunState = CPU.RunState.STATE_STOPPED_BAD_INSTR;
}
int cycles = dispatch();
executedCyclesPerSlot += cycles;
context.passedCycles(cycles);
} catch (Breakpoint e) {
return CPU.RunState.STATE_STOPPED_BREAK;
} catch (IndexOutOfBoundsException e) {
LOGGER.error("Unexpected error", e);
return CPU.RunState.STATE_STOPPED_ADDR_FALLOUT;
} catch (Throwable e) {
LOGGER.error("Unexpected error", e);
return CPU.RunState.STATE_STOPPED_BAD_INSTR;
return currentRunState;
}
}

long computationTime = System.nanoTime() - endTime;
delayNanos = slotNanos - computationTime;
}
return currentRunState;
);
}

private int dispatch() throws Throwable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,18 @@
*/
package net.emustudio.plugins.cpu.ssem;

import net.emustudio.emulib.plugins.cpu.CPU;
import net.emustudio.emulib.plugins.cpu.DecodedInstruction;
import net.emustudio.emulib.plugins.cpu.Decoder;
import net.emustudio.emulib.plugins.cpu.InvalidInstructionException;
import net.emustudio.emulib.plugins.cpu.*;
import net.emustudio.emulib.plugins.memory.MemoryContext;
import net.emustudio.emulib.runtime.helpers.Bits;
import net.emustudio.emulib.runtime.helpers.NumberUtils;
import net.emustudio.emulib.runtime.helpers.NumberUtils.Strategy;
import net.emustudio.emulib.runtime.helpers.SleepUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;

Expand All @@ -43,6 +38,9 @@
public class EmulatorEngine {
public final static double INSTRUCTION_TIME_MS = 1.44;
public final static double INSTRUCTIONS_PER_SECOND = 1.0 / (INSTRUCTION_TIME_MS / 1000.0);

private final static int INSTRUCTION_CYCLES = 100; // artificial number
public final static double FREQUENCY_KHZ = (INSTRUCTIONS_PER_SECOND / 1000.0) * INSTRUCTION_CYCLES;
final static int LINE_MASK = 0b11111000;
private final static Logger LOGGER = LoggerFactory.getLogger(EmulatorEngine.class);
private static final Method[] DISPATCH_TABLE = new Method[8];
Expand All @@ -67,6 +65,7 @@ public class EmulatorEngine {
private final Decoder decoder;
private final Function<Integer, Boolean> isBreakpointSet;
private final Bits emptyBits = new Bits(0, 0);
private final AccurateFrequencyRunner preciseRunner = new AccurateFrequencyRunner();

EmulatorEngine(MemoryContext<Byte> memory, Function<Integer, Boolean> isBreakpointSet) {
this.memory = Objects.requireNonNull(memory);
Expand Down Expand Up @@ -154,55 +153,37 @@ private void writeInt(int lineAddress, int value) {
memory.write(lineAddress, word);
}

@SuppressWarnings("BusyWait")
CPU.RunState run() {
// 1 instruction takes 1.44 ms (~700 instructions per second)
// 1 / (INSTRUCTION_TIME_MS / 1000) = 694.4444444444444 Hz

final long slotNanos = 2 * SleepUtils.SLEEP_PRECISION; // sleeping precision is usually ~1 ms
final double slotMillis = slotNanos / 1_000_000.0;
final int instructionsPerSlot = (int) (slotMillis / INSTRUCTION_TIME_MS);

CPU.RunState currentRunState = CPU.RunState.STATE_RUNNING;
long delayNanos = SleepUtils.SLEEP_PRECISION;

long startTime = System.nanoTime();
long executedInstructionsPerSlot = 0;

while (!Thread.currentThread().isInterrupted() && (currentRunState == CPU.RunState.STATE_RUNNING)) {
try {
if (delayNanos > 0) {
Thread.sleep(TimeUnit.NANOSECONDS.toMillis(delayNanos));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}

long endTime = System.nanoTime();
long targetInstructions = (endTime - startTime) / slotNanos * instructionsPerSlot;

while ((executedInstructionsPerSlot < targetInstructions) && !Thread.currentThread().isInterrupted() && (currentRunState == CPU.RunState.STATE_RUNNING)) {
try {
if (isBreakpointSet.apply(CI.get())) {
return CPU.RunState.STATE_STOPPED_BREAK;
}
currentRunState = step();
executedInstructionsPerSlot += 1;
} catch (IllegalArgumentException e) {
LOGGER.debug("Unexpected error", e);
if (e.getCause() != null && e.getCause() instanceof IndexOutOfBoundsException) {
// frequency = 1 / (INSTRUCTION_TIME_MS / 1000) = 694.44 Hz = 0.69444 kHz
// We need integer number of kHZ here, so we need to adjust number of instruction cycles to be > 1.
// Let's say 1 instruction = 100 cycles.
// Then, frequency = 100 * 694.44 Hz = 69444 Hz = 69.444 kHz

return preciseRunner.run(
() -> FREQUENCY_KHZ,
() -> {
try {
if (isBreakpointSet.apply(CI.get())) {
return CPU.RunState.STATE_STOPPED_BREAK;
}
preciseRunner.addExecutedCycles(INSTRUCTION_CYCLES);
return step();
} catch (IndexOutOfBoundsException e) {
LOGGER.error("Unexpected error", e);
return CPU.RunState.STATE_STOPPED_ADDR_FALLOUT;
} catch (IllegalArgumentException e) {
LOGGER.debug("Unexpected error", e);
if (e.getCause() != null && e.getCause() instanceof IndexOutOfBoundsException) {
return CPU.RunState.STATE_STOPPED_ADDR_FALLOUT;
} else {
return CPU.RunState.STATE_STOPPED_BAD_INSTR;
}
} catch (Throwable e) {
LOGGER.error("Unexpected error", e);
return CPU.RunState.STATE_STOPPED_BAD_INSTR;
}
return CPU.RunState.STATE_STOPPED_BAD_INSTR;
} catch (IndexOutOfBoundsException e) {
LOGGER.debug("Unexpected error", e);
return CPU.RunState.STATE_STOPPED_ADDR_FALLOUT;
}
}

long computationTime = System.nanoTime() - endTime;
delayNanos = slotNanos - computationTime;
}
return currentRunState;
);
}
}
Loading
Loading