Skip to content

Commit

Permalink
[#314] Use AccurateFrequencyRunner
Browse files Browse the repository at this point in the history
  • Loading branch information
vbmacher committed Dec 31, 2023
1 parent c5122ca commit 8994b6e
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 236 deletions.

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

0 comments on commit 8994b6e

Please sign in to comment.