Switching to new interrupt triggers
This commit is contained in:
parent
8315774b1d
commit
58d358b51e
7 changed files with 534 additions and 454 deletions
|
|
@ -199,46 +199,34 @@ BusPDP11.IOHANDLER = {
|
|||
/*
|
||||
* These are our custom IOController functions for all IOPAGE accesses. They look up the IOPAGE
|
||||
* offset in the aIOHandlers table, and if an entry exists, they use the appropriate IOHANDLER indexes
|
||||
* (above) to locate the appropriate read/write handlers.
|
||||
* (above) to locate the registered read/write handlers. If no handler is found, then unknownAccess()
|
||||
* is called, triggering a trap -- unless traps are disabled because direct access was requested
|
||||
* (eg, by the Debugger).
|
||||
*
|
||||
* Note that we try to have reasonable fallbacks for byte reads when only word read handlers exist,
|
||||
* and word reads if only byte handlers exist. Ditto for writes. These fallbacks may not always be
|
||||
* appropriate; for example, when a byte write falls back to a word write, the address must be read
|
||||
* first, and depending on the underlying I/O device, that may or may not have side-effects. It's really
|
||||
* up to the device to know whether that matters, and provide all the necessary handlers if it does.
|
||||
* Handlers receive the original IOPAGE address that was used, although in most cases, it's ignored,
|
||||
* because most handlers usually handle only one address. Only handlers used for a range of addresses
|
||||
* must pay attention to it.
|
||||
*
|
||||
* Note that these functions include fallbacks for byte reads when only word read handlers exist (by
|
||||
* masking or shifting the result) and for word reads if only byte handlers exist (by combining bytes).
|
||||
* Fallbacks for writes exist, too, but they are slightly more complicated, because a byte write using
|
||||
* a word write handler requires reading the word first, and then updating the appropriate byte within
|
||||
* that word.
|
||||
*
|
||||
* Those fallbacks may not always be appropriate; for example, byte writes to some device registers
|
||||
* must be zero-extended to update the entire word. For those cases, the fallback's "preliminary" read
|
||||
* is issued with a zero address so that the handler can distinguish a normal read from one of these
|
||||
* preliminary reads, and return an appropriate value for the update (ie, zero).
|
||||
*
|
||||
* If none of these fallback behaviors are appropriate, the device has a simple recourse: register
|
||||
* handlers for all possible addresses and sizes.
|
||||
*
|
||||
* Unlike regular Memory blocks, IOPAGE accesses permit word accesses on ODD addresses; that works
|
||||
* just fine by registering WORD handlers for the appropriate ODD addresses. What is unclear, however,
|
||||
* is what is exactly supposed to happen when the CPU reads or writes a BYTE from an ODD IOPAGE address;
|
||||
* it seems clear that our built-in fallbacks are NOT correct in all cases.
|
||||
* just fine by registering WORD handlers for the appropriate ODD addresses. For BYTE accesses, it
|
||||
* depends. For CPU register addresses, addIOHandlers() installs special byte handlers that perform
|
||||
* either a simple word read or write. Other addresses must be handled on case-by-case basis.
|
||||
*
|
||||
* For example, let's imagine that only read/write WORD handlers have been registered for ODD address
|
||||
* 177701 (ie, general register R1, set 0). If a readByte(177701) request is made, we would fallback to
|
||||
* reading the word at 177700 and returning the high byte, but that would fetch the contents of general
|
||||
* register R0 instead of R1, which is clearly wrong.
|
||||
*
|
||||
* One solution is for the caller to register both BYTE and WORD handlers for ODD addresses, making
|
||||
* the caller responsible for the defining the correct behavior in all cases. However, that's more work
|
||||
* for the caller, and we're just punting the problem instead of solving it.
|
||||
*
|
||||
* Another solution is for addIOHandlers() to detect the ODD address case, and install custom fallback
|
||||
* handlers for read and write BYTE accesses. In the above example, the custom read BYTE handler could
|
||||
* invoke the read WORD handler and mask the result with 0xff, and the custom write BYTE handler could
|
||||
* first call the read WORD handler, insert the new data into the low byte of the result, and then
|
||||
* call the write WORD handler.
|
||||
*
|
||||
* However, that would produce inconsistent results between EVEN and ODD addresses in the register
|
||||
* address range (0o177700 through 0o177717), so I'm assuming that the correct solution is to install
|
||||
* alternate fallback BYTE handlers for ALL addresses in that range. The alternate read BYTE handler will
|
||||
* mask its result with 0xff, and the alternate write BYTE handler will store the (zero-extended) byte to
|
||||
* the entire corresponding register.
|
||||
*
|
||||
* One of things that gives me pause about this solution is that it differs from the behavior of a MOVB
|
||||
* instruction when the destination is a general register, because MOVB always sign-extends the source byte
|
||||
* to a word; it does NOT zero-extend. So it seems to strange to have a different MOVB behavior when the
|
||||
* destination register is specified using an IOPAGE address. But, maybe that was by design.
|
||||
*
|
||||
* TODO: Another small potential improvement would be for addIOHandlers() to predefine fall-backs for all
|
||||
* TODO: Another small potential improvement would be for addIOHandlers() to install fallbacks for ALL
|
||||
* missing handlers, in both the ODD and EVEN cases, so there's never a need to check each function index
|
||||
* before calling it. However, since there's no avoiding checking aIOHandlers[off] (unless we FULLY populate
|
||||
* the aIOHandlers array), and since these I/O accesses should be pretty infrequent relative to all other
|
||||
|
|
@ -336,6 +324,10 @@ BusPDP11.IOController = {
|
|||
* If no handler existed, and this address was odd, then perhaps a handler exists for the even address;
|
||||
* if so, call the readWord() handler first to get the original data, then call writeWord() with the new
|
||||
* data pre-inserted in (the high byte of) the original data.
|
||||
*
|
||||
* WARNING: Whenever we call readWord() under these circumstances, we zero the address parameter,
|
||||
* so that the handler can distinguish this case. Thus, if we're dealing with a special register
|
||||
* where a byte write operation modifies the entire register, the handler can simply return zero.
|
||||
*/
|
||||
afn = bus.aIOHandlers[off & ~0x1];
|
||||
if (afn) {
|
||||
|
|
@ -484,7 +476,7 @@ BusPDP11.prototype.setIOPageRange = function(nRange)
|
|||
/**
|
||||
* getControllerBuffer(addr)
|
||||
*
|
||||
* Our Bus component also acts as custom memory controller, so it must also provide this function.
|
||||
* Our Bus component also acts as custom memory controller for the IOPAGE, so it must also provide this function.
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr
|
||||
|
|
@ -493,7 +485,7 @@ BusPDP11.prototype.setIOPageRange = function(nRange)
|
|||
BusPDP11.prototype.getControllerBuffer = function(addr)
|
||||
{
|
||||
/*
|
||||
* No buffer is required; all accesses go to a registered I/O handler or the bit-bucket.
|
||||
* No buffer is required for the IOPAGE; all accesses go to registered I/O handlers or to unknownAccess().
|
||||
*/
|
||||
return [null, 0];
|
||||
};
|
||||
|
|
@ -501,7 +493,7 @@ BusPDP11.prototype.getControllerBuffer = function(addr)
|
|||
/**
|
||||
* getControllerAccess()
|
||||
*
|
||||
* Our Bus component also acts as custom memory controller, so it must also provide this function.
|
||||
* Our Bus component also acts as custom memory controller for the IOPAGE, so it must also provide this function.
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @return {Array.<function()>}
|
||||
|
|
@ -511,6 +503,17 @@ BusPDP11.prototype.getControllerAccess = function()
|
|||
return this.afnIOPage;
|
||||
};
|
||||
|
||||
/**
|
||||
* getWidth()
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @return {number}
|
||||
*/
|
||||
BusPDP11.prototype.getWidth = function()
|
||||
{
|
||||
return this.nBusWidth;
|
||||
};
|
||||
|
||||
/**
|
||||
* reset()
|
||||
*
|
||||
|
|
@ -530,9 +533,7 @@ BusPDP11.prototype.reset = function()
|
|||
* unknownAccess(addr, data, byteFlag)
|
||||
*
|
||||
* This is our default I/O handler, called whenever there's an IOPAGE access without a corresponding entry
|
||||
* in aIOHandlers; in the interim, our Device component will override this default handler with its own function
|
||||
* by calling addIODefaultHandlers(), until the Device component has been completely rewritten to install I/O
|
||||
* handlers for all supported I/O addresses.
|
||||
* in aIOHandlers.
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @param {number} addr (ie, an IOPAGE address)
|
||||
|
|
@ -757,17 +758,6 @@ BusPDP11.prototype.scanMemory = function(info, addr, size)
|
|||
return info;
|
||||
};
|
||||
|
||||
/**
|
||||
* getWidth()
|
||||
*
|
||||
* @this {BusPDP11}
|
||||
* @return {number}
|
||||
*/
|
||||
BusPDP11.prototype.getWidth = function()
|
||||
{
|
||||
return this.nBusWidth;
|
||||
};
|
||||
|
||||
/**
|
||||
* removeMemory(addr, size)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -83,15 +83,37 @@ function CPUStatePDP11(parmsCPU)
|
|||
|
||||
Component.subclass(CPUStatePDP11, CPUPDP11);
|
||||
|
||||
/*
|
||||
* Overview of Device Interrupt Support
|
||||
*
|
||||
* Originally, the CPU maintained a queue of requested interrupts. Entries in this queue recorded a device's
|
||||
* priority, vector, and delay (ie, a number of instructions to process before issuing the interrupt). This queue
|
||||
* would constantly grow and shrink as requests were issued and dispatched, and as long as there was something
|
||||
* in the queue, the CPU was constantly examining it.
|
||||
*
|
||||
* Now we are trying something simpler. First, the CPU offers timer services to any device that wants a callback
|
||||
* after a specified delay, which are much more efficient than requiring the CPU to dive into an interrupt queue and
|
||||
* look for (and decrement) delay counts on every instruction.
|
||||
*
|
||||
* Second, when a device decides it's time to interrupt (either at the end of some operation or when its delay timer
|
||||
* has fired), it will simply request the interrupt, and CPU priority permitting, the interrupt will be dispatched.
|
||||
* If CPU priority is NOT permitting, then the interrupt will remain pending until the priority drops. This means
|
||||
* that the CPU need not check for any pending interrupts until/unless the CPU priority changes.
|
||||
*
|
||||
* All this will be managed with "triggers". Every device that wants to interrupt must register via addTrigger().
|
||||
* This will add a Trigger object to an array, and each object will contain the (self) assigned vector, the priority,
|
||||
* the interrupt request status, and a next pointer.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* delay: number,
|
||||
* priority: number,
|
||||
* vector: number,
|
||||
* callback: (function()|null|undefined)
|
||||
* }} InterruptEvent
|
||||
* priority: number,
|
||||
* request: number,
|
||||
* next: (Trigger|null)
|
||||
* }} Trigger
|
||||
*/
|
||||
var InterruptEvent;
|
||||
var Trigger;
|
||||
|
||||
/**
|
||||
* initProcessor()
|
||||
|
|
@ -101,7 +123,15 @@ var InterruptEvent;
|
|||
CPUStatePDP11.prototype.initProcessor = function()
|
||||
{
|
||||
this.decode = PDP11.op1170.bind(this);
|
||||
|
||||
/** @type {Array.<Trigger>} */
|
||||
this.aTriggers = [];
|
||||
|
||||
/** @type {Trigger|null} */
|
||||
this.triggerNext = null;
|
||||
|
||||
this.initRegs();
|
||||
|
||||
this.flags.complete = this.flags.debugCheck = false;
|
||||
};
|
||||
|
||||
|
|
@ -169,7 +199,7 @@ CPUStatePDP11.prototype.initRegs = function()
|
|||
this.regMB = 0;
|
||||
|
||||
/*
|
||||
* opFlags contains various triggers that stepCPU() needs to be aware of
|
||||
* opFlags contains various conditions that stepCPU() needs to be aware of
|
||||
*/
|
||||
this.opFlags = 0;
|
||||
|
||||
|
|
@ -208,8 +238,6 @@ CPUStatePDP11.prototype.resetRegs = function()
|
|||
this.mmuMask = 0x3ffff;
|
||||
this.mmuMemorySize = BusPDP11.IOPAGE_18BIT;
|
||||
|
||||
/** @type {Array.<InterruptEvent>} */
|
||||
this.interruptQueue = [];
|
||||
this.opFlags |= PDP11.OPFLAG.INTQ;
|
||||
|
||||
if (this.bus) this.setMemoryAccess();
|
||||
|
|
@ -562,77 +590,131 @@ CPUStatePDP11.prototype.setSP = function(addr)
|
|||
};
|
||||
|
||||
/**
|
||||
* interrupt(delay, priority, vector, callback)
|
||||
*
|
||||
* Interrupts are stored in a queue in delay order with the delay expressed as
|
||||
* a difference. For example if the delays were 0, 1, 0 then the first entry
|
||||
* is active and both the second and third are waiting for one more instruction
|
||||
* execution to become active.
|
||||
* addTrigger(vector, priority)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} delay
|
||||
* @param {number} priority
|
||||
* @param {number} vector
|
||||
* @param {function()} [callback]
|
||||
* @param {number} priority
|
||||
* @return {Trigger}
|
||||
*/
|
||||
CPUStatePDP11.prototype.interrupt = function(delay, priority, vector, callback)
|
||||
CPUStatePDP11.prototype.addTrigger = function(vector, priority)
|
||||
{
|
||||
var i = this.interruptQueue.length;
|
||||
while (i-- > 0) {
|
||||
if (this.interruptQueue[i].vector === vector) {
|
||||
if (i > 0) {
|
||||
this.interruptQueue[i - 1].delay += this.interruptQueue[i].delay;
|
||||
}
|
||||
this.interruptQueue.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (delay >= 0) {
|
||||
i = this.interruptQueue.length; // queue in delay 'difference' order
|
||||
while (i-- > 0) {
|
||||
if (this.interruptQueue[i].delay > delay) {
|
||||
this.interruptQueue[i].delay -= delay;
|
||||
break;
|
||||
}
|
||||
delay -= this.interruptQueue[i].delay;
|
||||
}
|
||||
/*
|
||||
* NOTE regarding a Google Closure Compiler "bug": if an InterruptEvent is inserted into the
|
||||
* interruptQueue with the named properties below, they will never be seen by the rest of the
|
||||
* code, because the compiler renames all other property references EXCEPT these.
|
||||
*
|
||||
* this.interruptQueue.splice(i + 1, 0, {
|
||||
* "delay": delay,
|
||||
* "priority": (priority << 5) & 0xe0,
|
||||
* "vector": vector,
|
||||
* "callback": callback
|
||||
* });
|
||||
*
|
||||
* Perhaps if the inlined object had been explicitly @typed, that wouldn't have happened, but
|
||||
* I'm playing it safe now: I've taken the object out-of-line, removed the quoted property names,
|
||||
* and explicitly typed it. The compiler re-inlines the object with correctly renamed properties.
|
||||
*/
|
||||
/** @type {InterruptEvent} */
|
||||
var interruptEvent = {
|
||||
delay: delay,
|
||||
priority: (priority << 5) & 0xe0,
|
||||
vector: vector,
|
||||
callback: callback
|
||||
};
|
||||
this.interruptQueue.splice(i + 1, 0, interruptEvent);
|
||||
}
|
||||
this.opFlags |= PDP11.OPFLAG.INTQ;
|
||||
var trigger = {vector: vector, priority: priority, request: -1, next: null};
|
||||
this.aTriggers.push(trigger);
|
||||
return trigger;
|
||||
};
|
||||
|
||||
/**
|
||||
* checkInterruptQueue()
|
||||
* checkTriggers()
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
*/
|
||||
CPUStatePDP11.prototype.checkInterruptQueue = function()
|
||||
CPUStatePDP11.prototype.checkTriggers = function()
|
||||
{
|
||||
if (this.triggerNext && this.setTrigger(this.triggerNext)) {
|
||||
this.removeTrigger(this.triggerNext);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* insertTrigger(trigger)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {Trigger} trigger
|
||||
*/
|
||||
CPUStatePDP11.prototype.insertTrigger = function(trigger)
|
||||
{
|
||||
if (trigger != this.triggerNext) {
|
||||
var triggerPrev = this.triggerNext;
|
||||
if (!triggerPrev || triggerPrev.priority <= trigger.priority) {
|
||||
trigger.next = triggerPrev;
|
||||
this.triggerNext = trigger;
|
||||
} else {
|
||||
do {
|
||||
var triggerNext = triggerPrev.next;
|
||||
if (!triggerNext || triggerNext.priority <= trigger.priority) {
|
||||
trigger.next = triggerNext;
|
||||
triggerPrev.next = trigger;
|
||||
break;
|
||||
}
|
||||
triggerPrev = triggerNext;
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* removeTrigger(trigger)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {Trigger} trigger
|
||||
*/
|
||||
CPUStatePDP11.prototype.removeTrigger = function(trigger)
|
||||
{
|
||||
var triggerPrev = this.triggerNext;
|
||||
if (triggerPrev == trigger) {
|
||||
this.triggerNext = trigger.next;
|
||||
} else {
|
||||
do {
|
||||
var triggerNext = triggerPrev.next;
|
||||
if (triggerNext == trigger) {
|
||||
triggerPrev.next = triggerNext.next;
|
||||
break;
|
||||
}
|
||||
triggerPrev = triggerNext;
|
||||
} while (true);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* setTrigger(trigger)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {Trigger|null} trigger
|
||||
* @return {boolean} (true if interrupt dispatched, false if not)
|
||||
*/
|
||||
CPUStatePDP11.prototype.setTrigger = function(trigger)
|
||||
{
|
||||
if (!this.dispatchInterrupt(trigger.vector, trigger.priority)) {
|
||||
this.insertTrigger(trigger);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* dispatchInterrupt(vector, priority)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} vector
|
||||
* @param {number} priority
|
||||
* @return {boolean} (true if dispatched, false if not)
|
||||
*/
|
||||
CPUStatePDP11.prototype.dispatchInterrupt = function(vector, priority)
|
||||
{
|
||||
var priorityCPU = (this.regPSW & PDP11.PSW.PRI) >> PDP11.PSW.SHIFT.PRI;
|
||||
if (priority > priorityCPU) {
|
||||
if (this.opFlags & PDP11.OPFLAG.WAIT) {
|
||||
this.advancePC(2);
|
||||
this.opFlags &= ~PDP11.OPFLAG.WAIT;
|
||||
}
|
||||
this.trap(vector, PDP11.REASON.INTERRUPT);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* checkInterrupts()
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
*/
|
||||
CPUStatePDP11.prototype.checkInterrupts = function()
|
||||
{
|
||||
if (this.opFlags & PDP11.OPFLAG.INTQ) {
|
||||
this.opFlags &= ~PDP11.OPFLAG.INTQ;
|
||||
this.checkTriggers();
|
||||
/*
|
||||
var interruptEvent = null;
|
||||
var savePSW = this.regPIR & 0xe0;
|
||||
for (var i = this.interruptQueue.length; --i >= 0;) {
|
||||
|
|
@ -666,6 +748,7 @@ CPUStatePDP11.prototype.checkInterruptQueue = function()
|
|||
this.trap(interruptEvent.vector, PDP11.REASON.INTERRUPT);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
else if (this.opFlags & PDP11.OPFLAG.INTQ_SPL) {
|
||||
/*
|
||||
|
|
@ -735,7 +818,7 @@ CPUStatePDP11.prototype.setPSW = function(newPSW)
|
|||
this.regsGen[6] = this.regsAltStack[this.mmuMode];
|
||||
}
|
||||
/*
|
||||
* Trigger a call to checkInterruptQueue()
|
||||
* Trigger a call to checkInterrupts()
|
||||
*/
|
||||
this.opFlags |= PDP11.OPFLAG.INTQ;
|
||||
this.regPSW = newPSW;
|
||||
|
|
@ -1017,7 +1100,7 @@ CPUStatePDP11.prototype.trap = function(vector, reason)
|
|||
}
|
||||
}
|
||||
|
||||
throw vector;
|
||||
if (reason != PDP11.REASON.INTERRUPT) throw vector;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1891,7 +1974,7 @@ CPUStatePDP11.prototype.stepCPU = function(nMinCycles)
|
|||
* interrupting the natural flow of instructions whenever the Debugger is stepping through code.
|
||||
*/
|
||||
if ((this.opFlags & (PDP11.OPFLAG.INTQ_SPL | PDP11.OPFLAG.INTQ | PDP11.OPFLAG.WAIT)) /*&& nMinCycles*/) {
|
||||
this.checkInterruptQueue();
|
||||
this.checkInterrupts();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -506,7 +506,6 @@ var PDP11 = {
|
|||
PRI: 4,
|
||||
RVEC: 0o60,
|
||||
XVEC: 0o64,
|
||||
DELAY: 40,
|
||||
RCSR: { // 177560
|
||||
RE: 0x0001, // Reader Enable (W/O) TODO: Determine if we really need to exclude this write-only bit from RMASK
|
||||
DTR: 0x0002, // Data Terminal Ready (R/W)
|
||||
|
|
@ -529,7 +528,8 @@ var PDP11 = {
|
|||
PARITY: 0x1000, // Received Data Parity (R/O)
|
||||
FE: 0x2000, // Framing Error (R/O)
|
||||
OE: 0x4000, // Overrun Error (R/O)
|
||||
ERROR: 0x8000 // Error (R/O)
|
||||
ERROR: 0x8000, // Error (R/O)
|
||||
DELAY: 1
|
||||
},
|
||||
XCSR: { // 177564
|
||||
BREAK: 0x0001, // BREAK (R/W)
|
||||
|
|
@ -538,11 +538,11 @@ var PDP11 = {
|
|||
READY: 0x0080, // Transmitter Ready (R/O)
|
||||
RMASK: 0x00C5,
|
||||
WMASK: 0x0045,
|
||||
DELAY: 8
|
||||
DELAY: 1
|
||||
},
|
||||
XBUF: { // 177566
|
||||
DATA: 0x00FF, // Transmitted Data (W/O) TODO: Determine why pdp11.js effectively defined this as 0x7F
|
||||
DELAY: 100
|
||||
DELAY: 1
|
||||
}
|
||||
},
|
||||
KW11: { // KW11-L Line Time Clock
|
||||
|
|
|
|||
|
|
@ -125,10 +125,12 @@ DevicePDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
this.dbg = dbg;
|
||||
|
||||
var device = this;
|
||||
this.kw11.timer = this.cpu.addTimer(function() {
|
||||
this.kw11.timer = cpu.addTimer(function() {
|
||||
device.kw11_interrupt();
|
||||
});
|
||||
|
||||
this.kw11.trigger = cpu.addTrigger(PDP11.KW11.VEC, PDP11.KW11.PRI);
|
||||
|
||||
bus.addIOTable(this, DevicePDP11.UNIBUS_IOTABLE);
|
||||
bus.addResetHandler(this.reset.bind(this));
|
||||
|
||||
|
|
@ -1008,7 +1010,7 @@ DevicePDP11.prototype.kw11_interrupt = function()
|
|||
{
|
||||
this.kw11.lks |= PDP11.KW11.LKS.MON;
|
||||
if (this.kw11.lks & PDP11.KW11.LKS.IE) {
|
||||
this.cpu.interrupt(PDP11.KW11.DELAY, PDP11.KW11.PRI, PDP11.KW11.VEC);
|
||||
this.cpu.setTrigger(this.kw11.trigger);
|
||||
this.cpu.setTimer(this.kw11.timer, 1000/60);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -260,22 +260,28 @@ SerialPortPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
|
||||
var serial = this;
|
||||
|
||||
this.timerReceiveData = this.cpu.addTimer(function() {
|
||||
this.triggerReceiveInterrupt = this.cpu.addTrigger(PDP11.DL11.RVEC, PDP11.DL11.PRI);
|
||||
|
||||
this.timerReceiveInterrupt = this.cpu.addTimer(function() {
|
||||
if (!(serial.rcsr & PDP11.DL11.RCSR.RD)) {
|
||||
if (serial.abReceive.length) {
|
||||
serial.rbuf = serial.abReceive.shift();
|
||||
serial.rcsr |= PDP11.DL11.RCSR.RD;
|
||||
if (serial.rcsr & PDP11.DL11.RCSR.RIE) {
|
||||
serial.cpu.interrupt(PDP11.DL11.DELAY, PDP11.DL11.PRI, PDP11.DL11.RVEC);
|
||||
serial.cpu.setTrigger(serial.triggerReceiveInterrupt);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.doneTransmitInterrupt = function() {
|
||||
this.triggerTransmitInterrupt = this.cpu.addTrigger(PDP11.DL11.XVEC, PDP11.DL11.PRI);
|
||||
|
||||
this.timerTransmitInterrupt = this.cpu.addTimer(function() {
|
||||
serial.xcsr |= PDP11.DL11.XCSR.READY;
|
||||
return !!(serial.xcsr & PDP11.DL11.XCSR.TIE);
|
||||
};
|
||||
if (serial.xcsr & PDP11.DL11.XCSR.TIE) {
|
||||
serial.cpu.setTrigger(serial.triggerTransmitInterrupt);
|
||||
}
|
||||
});
|
||||
|
||||
bus.addIOTable(this, SerialPortPDP11.UNIBUS_IOTABLE);
|
||||
this.setReady();
|
||||
|
|
@ -465,7 +471,7 @@ SerialPortPDP11.prototype.receiveData = function(data)
|
|||
else {
|
||||
this.abReceive = this.abReceive.concat(data);
|
||||
}
|
||||
this.cpu.setTimer(this.timerReceiveData, 50);
|
||||
this.cpu.setTimer(this.timerReceiveInterrupt, PDP11.DL11.RBUF.DELAY);
|
||||
return true; // for now, return true regardless, since we're buffering everything anyway
|
||||
};
|
||||
|
||||
|
|
@ -568,7 +574,7 @@ SerialPortPDP11.prototype.readRBUF = function(addr)
|
|||
{
|
||||
this.rcsr &= ~PDP11.DL11.RCSR.RD;
|
||||
if (this.abReceive.length > 0) {
|
||||
this.cpu.setTimer(this.timerReceiveData, 50);
|
||||
this.cpu.setTimer(this.timerReceiveInterrupt, PDP11.DL11.RBUF.DELAY);
|
||||
}
|
||||
return this.rbuf;
|
||||
};
|
||||
|
|
@ -609,7 +615,7 @@ SerialPortPDP11.prototype.writeXCSR = function(data, addr)
|
|||
* If the device is READY, and IE is transitioning on, then request an interrupt.
|
||||
*/
|
||||
if ((this.xcsr & (PDP11.DL11.XCSR.READY | PDP11.DL11.XCSR.TIE)) == PDP11.DL11.XCSR.READY && (data & PDP11.DL11.XCSR.TIE)) {
|
||||
this.cpu.interrupt(PDP11.DL11.XCSR.DELAY, PDP11.DL11.PRI, PDP11.DL11.XVEC, this.doneTransmitInterrupt);
|
||||
this.cpu.setTimer(this.timerTransmitInterrupt, PDP11.DL11.XCSR.DELAY);
|
||||
}
|
||||
this.xcsr = (this.xcsr & ~PDP11.DL11.XCSR.WMASK) | (data & PDP11.DL11.XCSR.WMASK);
|
||||
};
|
||||
|
|
@ -639,7 +645,7 @@ SerialPortPDP11.prototype.writeXBUF = function(data, addr)
|
|||
if (data) {
|
||||
this.transmitByte(data);
|
||||
this.xcsr &= ~PDP11.DL11.XCSR.READY;
|
||||
this.cpu.interrupt(PDP11.DL11.XBUF.DELAY, PDP11.DL11.PRI, PDP11.DL11.XVEC, this.doneTransmitInterrupt);
|
||||
this.cpu.setTimer(this.timerTransmitInterrupt, PDP11.DL11.XBUF.DELAY);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue