Skip to content
This repository has been archived by the owner on Feb 21, 2024. It is now read-only.

Commit

Permalink
Core/Hardware: don't call Device.step() every step
Browse files Browse the repository at this point in the history
Removed Device.step() entirely. Instead, devices can call Cpu.scheduleWake() to schedule a callback to the new Device.wake() method. All hardware devices modified to use scheduleWake()/wake() instead of step().
  • Loading branch information
branlwyd committed Dec 24, 2012
1 parent b4eef6d commit e204532
Show file tree
Hide file tree
Showing 6 changed files with 241 additions and 190 deletions.
177 changes: 138 additions & 39 deletions src/cc/bran/bdcpu16/Cpu.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,24 @@ public class Cpu
public static final int MAX_ADDRESS = Character.MAX_VALUE + 1; /* memory size in words -- equals number of values a char can take on -- 0x10000 */
private static final int MAX_SIMULTANEOUS_INTERRUPTS = 256;
private static final int DEFAULT_CLOCKSPEED = 100000;
private static final int STARTING_WAKE_REQUEST_COUNT = 16;
private static final InstructionProvider DEFAULT_INSTRUCTION_PROVIDER = new InstructionCompiler();

/* CPU state variables */
private State state;
private final int clockSpeed;
private char[] mem;
private char rA, rB, rC, rX, rY, rZ, rI, rJ;
private char pc, sp, ex, ia;
private boolean skip;
private int timestamp;

private boolean interruptsEnabled;
private Node<Character> intHead, intTail;

private final int clockSpeed;
private final Node<Device> devHead;
private final Device[] attachedDevices;
private DeviceWakeRequest wakeHead;
private DeviceWakeRequest freeHead;

private StepHandler specialHandler;
private final StepHandler interruptHandler;
Expand Down Expand Up @@ -116,8 +119,7 @@ public Cpu(int clockSpeed, Device[] attachedDevices, InstructionProvider instPro
/* basic state */
state = State.RUNNING;
mem = new char[MAX_ADDRESS];
rA = rB = rC = rX = rY = rZ = rI = rJ = pc = sp = ex = ia = 0;
skip = false;
timestamp = Integer.MIN_VALUE;

/* generate special instruction handlers */
interruptHandler = new InterruptStepHandler();
Expand All @@ -137,31 +139,26 @@ public Cpu(int clockSpeed, Device[] attachedDevices, InstructionProvider instPro
}
intHead.next = curIntNode;

DeviceWakeRequest[] wakeRequests = new DeviceWakeRequest[STARTING_WAKE_REQUEST_COUNT];
wakeRequests[0] = new DeviceWakeRequest();
for(int i = 1; i < STARTING_WAKE_REQUEST_COUNT; ++i)
{
wakeRequests[i] = new DeviceWakeRequest();
wakeRequests[i].freeNext = wakeRequests[i-1];
}
freeHead = wakeRequests[STARTING_WAKE_REQUEST_COUNT - 1];

/* handle parameters */
this.clockSpeed = clockSpeed;
this.instProvider = instProvider;

/* set up devHead linked list */
if(attachedDevices != null && attachedDevices.length > 0)

if(attachedDevices != null)
{
this.attachedDevices = Arrays.copyOf(attachedDevices, attachedDevices.length);

Node<Device> curDevNode = new Node<Device>();
curDevNode.value = attachedDevices[0];
for(int i = 1; i < attachedDevices.length; ++i)
{
Node<Device> newDevNode = new Node<Device>();
newDevNode.value = attachedDevices[i];
newDevNode.next = curDevNode;

curDevNode = newDevNode;
}
devHead = curDevNode;
}
else
{
this.attachedDevices = new Device[0];
devHead = null;
}
}

Expand All @@ -187,12 +184,25 @@ public int step()
cyclesUsed = inst.execute(this);
}

/* step devices */
Node<Device> node = devHead;
while(node != null)
/* wake any devices that are ready */
if(wakeHead != null)
{
node.value.step(cyclesUsed);
node = node.next;
timestamp += cyclesUsed;

while(timestamp - wakeHead.endTimestamp > 0)
{
wakeHead.freeNext = freeHead;
freeHead = wakeHead;
wakeHead = wakeHead.wakeNext;

/* at this point, freeHead is the just-removed wakeHead */
freeHead.device.wake(timestamp - freeHead.startTimestamp, freeHead.context);

if(wakeHead == null)
{
break;
}
}
}

return cyclesUsed;
Expand Down Expand Up @@ -221,6 +231,92 @@ public void interrupt(char message)
}
}

/**
* A device can call this method to request that the CPU wake it after a given number of cycles have
* elapsed. Note that the CPU may not notify after exactly the correct number of cycles--e.g., if the
* triggering cycle falls in the middle of an instruction, the device will not be notified until the
* end of the instruction.
* @param device the device to wake up
* @param cycles the number of cycles before the device should be woken
*/
public void scheduleWake(Device device, int cycles)
{
scheduleWake(device, cycles, 0);
}

/**
* A device can call this method to request that the CPU wake it after a given number of cycles have
* elapsed. Note that the CPU may not notify after exactly the correct number of cycles--e.g., if the
* triggering cycle falls in the middle of an instruction, the device will not be notified until the
* end of the instruction. The device can also pass an arbitrary numeric context value that will be
* passed back to the device upon notification.
* @param device the device to wake up
* @param cycles the number of cycles before the device should be woken
* @param context the context value to be passed back to the device upon wakeup
*/
public void scheduleWake(Device device, int cycles, int context)
{
if(freeHead == null)
{
/*
* we ran out of free wake requests, so we need to make some.
*
* we want to double the number of current requests, but we don't keep track of the total number of
* wake requests. since every wake request is in the wakelist, we can traverse the list and create
* a new request for each existing request.
*/

DeviceWakeRequest req = wakeHead.wakeNext;
freeHead = new DeviceWakeRequest();
DeviceWakeRequest freeEnd = freeHead;
while(req != null)
{
freeEnd.freeNext = new DeviceWakeRequest();
freeEnd = freeEnd.freeNext;

req = req.wakeNext;
}
}

final DeviceWakeRequest req = freeHead;
freeHead = freeHead.freeNext;

final int endTimestamp = timestamp + cycles;
req.device = device;
req.context = context;
req.startTimestamp = timestamp;
req.endTimestamp = endTimestamp;

/* if there are no entries, or we come before the first entry, make us the head of the wake list */
if(wakeHead == null || (endTimestamp - wakeHead.endTimestamp) <= 0)
{
req.wakeNext = wakeHead;
wakeHead = req;
return;
}

/* walk the wakelist looking for correct position */
DeviceWakeRequest cur = wakeHead;
while(cur.wakeNext != null && (cur.wakeNext.endTimestamp - endTimestamp) < 0)
{
cur = cur.wakeNext;
}

/* cur directly preceds req in the queue; add req */
req.wakeNext = cur.wakeNext;
cur.wakeNext = req;
}

/**
* This method is called by IllegalInstruction.execute() to notify the CPU that it just tried to
* execute an illegal instruction. This method puts the CPU in an error state.
*/
void illegalInstructionExecuted()
{
state = State.ERROR_ILLEGAL_INSTRUCTION;
specialHandler = errorHandler;
}

/**
* Gets the state of the CPU.
* @return the state of the CPU
Expand Down Expand Up @@ -611,16 +707,6 @@ public int clockSpeed()
return clockSpeed;
}

/**
* This method is called by IllegalInstruction.execute() to notify the CPU that it just tried to
* execute an illegal instruction. This method just puts the CPU in an error state.
*/
void illegalInstructionExecuted()
{
state = State.ERROR_ILLEGAL_INSTRUCTION;
specialHandler = errorHandler;
}

/**
* Represents the states that the CPU can be in.
* @author Brandon Pitman
Expand Down Expand Up @@ -662,6 +748,21 @@ private class Node<T>
public T value;
public Node<T> next;
}

/**
* This class tracks a device's request to be notified (woken up) in a certain number of cycles.
* @author Brandon Pitman
*/
private class DeviceWakeRequest
{
public Device device;
public int startTimestamp;
public int endTimestamp;
public int context;

public DeviceWakeRequest wakeNext;
public DeviceWakeRequest freeNext;
}

/**
* This interface is implemented by classes that provide "special" CPU-stepping
Expand Down Expand Up @@ -692,11 +793,9 @@ private class InitializationStepHandler implements StepHandler
public int step()
{
/* attach hardware devices... */
Node<Device> dev = devHead;
while(dev != null)
for(int i = 0; i < attachedDevices.length; ++i)
{
dev.value.attach(Cpu.this);
dev = dev.next;
attachedDevices[i].attach(Cpu.this);
}

/* ...then just run standard logic */
Expand Down
9 changes: 7 additions & 2 deletions src/cc/bran/bdcpu16/debug/Debugger.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ public void attach(Cpu cpu)
{
beginPause();
}

/* asking to wake in 0 cycles will lead to us being woken on the next step() */
cpu.scheduleWake(this, 0);
}

@Override
Expand All @@ -61,9 +64,9 @@ public int interrupt()

return 0;
}

@Override
public void step(int cycleCount)
public void wake(int cycles, int context)
{
if(breakpoint(cpu.PC()) || cpu.error())
{
Expand All @@ -74,6 +77,8 @@ public void step(int cycleCount)
{
beginPause();
}

cpu.scheduleWake(this, 0);
}

@Override
Expand Down
69 changes: 27 additions & 42 deletions src/cc/bran/bdcpu16/hardware/Clock.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,44 @@ public class Clock implements Device
private Cpu cpu;
private char interruptMessage;

private int denom;
private int cycles;
private int curContext;
private int ticks;
private int totalTicks;
private int waitCycles;

public Clock()
{
interruptMessage = 0;
curContext = 0;
ticks = 0;
waitCycles = 0;
}

@Override
public void attach(Cpu cpu)
{
this.cpu = cpu;

interruptMessage = 0;
denom = 0;
cycles = 0;
ticks = 0;
totalTicks = 0;
}

@Override
public int interrupt()
{
switch(cpu.A())
{
case 0:
denom = cpu.B() * cpu.clockSpeed();
cycles = 0;
case 0: /* set rate */
curContext++;
ticks = 0;
totalTicks = 0;
waitCycles = cpu.clockSpeed() * cpu.B() / BASIC_RATE;
if(waitCycles != 0)
{
cpu.scheduleWake(this, waitCycles, curContext);
}
break;

case 1:
cpu.C((char)totalTicks);
case 1: /* get tick count */
cpu.C((char)ticks);
break;

case 2:
case 2: /* set interrupt message */
interruptMessage = cpu.B();
break;
}
Expand Down Expand Up @@ -76,39 +80,20 @@ public int manufacturer()
}

@Override
public void step(int cycleCount)
public void wake(int cycles, int context)
{
/*
* the basic logic used is: for any given number of total cycles, we should see (BASIC_RATE * cycles) / (clockspeed * B) ticks.
* compute the number of ticks we should have seen, and if it's greater than the number of ticks we've actually seen,
* increment the number of ticks appropriately. also, fix up cycles so that it doesn't get too big.
*/

if(denom == 0)
if(context != curContext)
{
/* this wake is for a previous rate */
return;
}

cycles += cycleCount;
int newTicks = (BASIC_RATE * cycles) / denom - ticks;
ticks += newTicks;
totalTicks += newTicks;

if(cycles > denom)
{
/*
* every denom cycles, we have had exactly BASIC_RATE * denom ticks, so these quantities are safe to remove together
*/
cycles -= denom;
ticks -= BASIC_RATE * denom;
}

ticks++;
if(interruptMessage != 0)
{
while(newTicks-- > 0)
{
cpu.interrupt(interruptMessage);
}
cpu.interrupt(interruptMessage);
}

cpu.scheduleWake(this, waitCycles, curContext);
}
}
Loading

0 comments on commit e204532

Please sign in to comment.