Improved keyboard compatibility with apps that hook the keyboard interrupt (eg, BASICA)
This commit is contained in:
parent
f5c0930f83
commit
3cc7476ddc
12 changed files with 2785 additions and 2733 deletions
|
|
@ -132,6 +132,7 @@
|
|||
F000:2FC8 @ KEYBOARD_IO_1
|
||||
F000:3054 @ KB_INT_1
|
||||
F000:30A9 @ K16
|
||||
F000:3460 @ SHIP_IT
|
||||
F000:346F @ PRINTER_IO_1
|
||||
F000:34F5 @ RS232_IO_1
|
||||
F000:3605 @ VIDEO_IO_1
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<title>IBM PC AT References</title>
|
||||
<document href="http://minuszerodegrees.net/manuals/IBM_5170_Technical_Reference_1502243_MAR84.pdf">
|
||||
<name>IBM 5170 Technical Reference (March 1984)</name>
|
||||
<cover href="../../../../../../../pubs/pc/reference/ibm/static/5170/techref/thumbs/IBM-5170-TECHREF 1.jpeg"/>
|
||||
<cover href="../../../../../../../pubs/pc/reference/ibm/static/5170/techref/1984-03/thumbs/IBM-5170-TECHREF 1.jpeg"/>
|
||||
<page href="#page=48">I/O Address Map</page>
|
||||
<page href="#page=67">CMOS Addresses</page>
|
||||
<page href="#page=165">BIOS Map</page>
|
||||
|
|
@ -20,4 +20,4 @@
|
|||
<page href="#page=27">Memory Organization and Segmentation</page>
|
||||
<page href="#page=321">VERR/VERW</page>
|
||||
</document>
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -3043,7 +3043,7 @@ ChipSet.prototype.outPICLo = function(iPIC, bOut, addrFrom)
|
|||
var nIRQ = (nIRL == null? undefined : pic.nIRQBase + nIRL);
|
||||
if (pic.bISR & bIREnd) {
|
||||
if (DEBUG && this.messageEnabled(this.messageBitsIRQ(nIRQ))) {
|
||||
this.printMessage("outPIC" + iPIC + '(' + str.toHexByte(pic.port) + "): IRQ " + nIRQ + " ending @" + this.dbg.hexOffset(this.cpu.getIP(), this.cpu.getCS()) + " stack=" + this.dbg.hexOffset(this.cpu.getSP(), this.cpu.getSS()), true);
|
||||
this.printMessage("outPIC" + iPIC + '(' + str.toHexByte(pic.port) + "): IRQ " + nIRQ + " ending @" + this.dbg.hexOffset(this.cpu.getIP(), this.cpu.getCS()) + " stack=" + this.dbg.hexOffset(this.cpu.getSP(), this.cpu.getSS()), true);
|
||||
}
|
||||
pic.bISR &= ~bIREnd;
|
||||
this.checkIRR();
|
||||
|
|
@ -3915,7 +3915,7 @@ ChipSet.prototype.outPPIB = function(port, bOut, addrFrom)
|
|||
{
|
||||
this.printMessageIO(port, bOut, addrFrom, "PPI_B");
|
||||
this.updatePPIB(bOut);
|
||||
if (this.kbd) this.kbd.setEnable((bOut & ChipSet.PPI_B.CLEAR_KBD)? false : true, (bOut & ChipSet.PPI_B.CLK_KBD)? true : false);
|
||||
if (this.kbd) this.kbd.setEnabled((bOut & ChipSet.PPI_B.CLEAR_KBD)? false : true, (bOut & ChipSet.PPI_B.CLK_KBD)? true : false);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -4040,6 +4040,26 @@ ChipSet.prototype.outPPICtrl = function(port, bOut, addrFrom)
|
|||
/**
|
||||
* in8042OutBuff(port, addrFrom)
|
||||
*
|
||||
* Return the contents of the OUTBUFF register and clear the OUTBUFF_FULL status bit.
|
||||
*
|
||||
* This function then calls kbd.checkScanCode(), on the theory that the next buffered scan
|
||||
* code, if any, can now be delivered to OUTBUFF. However, there are applications like
|
||||
* BASICA that install a keyboard interrupt handler that reads OUTBUFF, do some scan code
|
||||
* preprocessing, and then pass control on to the ROM's interrupt handler. As a result,
|
||||
* OUTBUFF is read multiple times during a single interrupt, so filling it with new data
|
||||
* after every read would result in lost scan codes.
|
||||
*
|
||||
* To avoid that problem, kbd.checkScanCode() also requires that kbd.setEnabled() be called
|
||||
* before it supplies any more data via notifyKbdData(). That will happen as soon as the
|
||||
* ROM re-enables the controller, and is why KBC.CMD.ENABLE_KBD processing also ends with a
|
||||
* call to kbd.checkScanCode().
|
||||
*
|
||||
* Note that, the foregoing notwithstanding, I still clear the OUTBUFF_FULL bit here (as I
|
||||
* believe I should); fortunately, none of the interrupt handlers rely on OUTBUFF_FULL as a
|
||||
* prerequisite for reading OUTBUFF (not the BASICA handler, and not the ROM). The assumption
|
||||
* seems to be that if an interrupt occurred, OUTBUFF must contain data, regardless of the
|
||||
* state of OUTBUFF_FULL.
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {number} port (0x60)
|
||||
* @param {number} [addrFrom] (not defined if the Debugger is trying to read the specified port)
|
||||
|
|
@ -4299,8 +4319,8 @@ ChipSet.prototype.out8042InBuffCmd = function(port, bOut, addrFrom)
|
|||
|
||||
case ChipSet.KBC.CMD.ENABLE_KBD: // 0xAE
|
||||
this.set8042CmdData(this.b8042CmdData & ~ChipSet.KBC.DATA.CMD.NO_CLOCK);
|
||||
if (this.kbd) this.kbd.checkScanCode();
|
||||
if (DEBUG) this.printMessage("keyboard re-enabled", Messages.KEYBOARD | Messages.PORT);
|
||||
if (this.kbd) this.kbd.checkScanCode();
|
||||
break;
|
||||
|
||||
case ChipSet.KBC.CMD.SELF_TEST: // 0xAA
|
||||
|
|
@ -4319,7 +4339,7 @@ ChipSet.prototype.out8042InBuffCmd = function(port, bOut, addrFrom)
|
|||
if (bPulseBits & 0x1) {
|
||||
/*
|
||||
* Bit 0 of the 8042's output port is connected to RESET. If it's pulsed, the processor resets.
|
||||
* We don't want to clear ALL our internal state (eg, cycle counts), so we call cpu.resetRegs() instead
|
||||
* We don't want to clear *all* CPU state (eg, cycle counts), so we call cpu.resetRegs() instead
|
||||
* of cpu.reset().
|
||||
*/
|
||||
this.cpu.resetRegs();
|
||||
|
|
@ -4354,7 +4374,7 @@ ChipSet.prototype.set8042CmdData = function(b)
|
|||
* and enables the keyboard. The BIOS then waits for OUTBUFF_FULL to be set, at which point it seems
|
||||
* to be anticipating an 0xAA response in the output buffer.
|
||||
*
|
||||
* And indeed, if we call the original MODEL_5150/MODEL_5160 setEnable() Keyboard interface here,
|
||||
* And indeed, if we call the original MODEL_5150/MODEL_5160 setEnabled() Keyboard interface here,
|
||||
* and both the data and clock lines have transitioned high (ie, both parameters are true), then it
|
||||
* will call resetDevice(), generating a Keyboard.CMDRES.BAT_OK response.
|
||||
*
|
||||
|
|
@ -4364,7 +4384,7 @@ ChipSet.prototype.set8042CmdData = function(b)
|
|||
* a completion code (eg, 0xAA for success, or 0xFC or something else for failure).
|
||||
*/
|
||||
var bClockEnabled = !(b & ChipSet.KBC.DATA.CMD.NO_CLOCK);
|
||||
this.kbd.setEnable(!!(b & ChipSet.KBC.DATA.CMD.NO_INHIBIT), bClockEnabled);
|
||||
this.kbd.setEnabled(!!(b & ChipSet.KBC.DATA.CMD.NO_INHIBIT), bClockEnabled);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -4507,7 +4527,7 @@ ChipSet.prototype.notifyKbdData = function(b)
|
|||
this.kbd.shiftScanCode();
|
||||
/*
|
||||
* A delay of 4 instructions was originally requested as part of the the Keyboard's resetDevice()
|
||||
* response, but a much larger delay (120) is now needed for MODEL_5170 machines, per the discussion above.
|
||||
* response, but a larger delay (120) is now needed for MODEL_5170 machines, per the discussion above.
|
||||
*/
|
||||
this.setIRR(ChipSet.IRQ.KBD, 120);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2289,7 +2289,6 @@ if (DEBUGGER) {
|
|||
aAddr[0] += inc;
|
||||
var limit = this.getSegment(aAddr[1]).limit;
|
||||
if (aAddr[0] > limit) {
|
||||
this.assert(false);
|
||||
aAddr[0] = 0;
|
||||
aAddr[2] = null;
|
||||
}
|
||||
|
|
@ -2527,13 +2526,6 @@ if (DEBUGGER) {
|
|||
}
|
||||
aAddr[2] = addr;
|
||||
}
|
||||
/*
|
||||
* Finally, we would map all addresses in the top 64Kb (of the 80286's 16Mb address space) to the top of
|
||||
* the 1Mb range, but now that mapBreakpoint() takes care of that in a processor-agnostic manner, at least
|
||||
* for breakpoint addresses, it seems unnecessary for getAddr() to do this anymore.
|
||||
*
|
||||
* if ((addr & 0xFF0000) == 0xFF0000) addr &= 0x0FFFFF;
|
||||
*/
|
||||
return addr;
|
||||
};
|
||||
|
||||
|
|
@ -3566,7 +3558,20 @@ if (DEBUGGER) {
|
|||
if (segment !== undefined) {
|
||||
aAddr[0] = offset;
|
||||
aAddr[1] = segment;
|
||||
symbol['p'] = this.getAddr(aAddr);
|
||||
aAddr[2] = null;
|
||||
/*
|
||||
* getAddr() computes the corresponding physical address and saves it in aAddr[2].
|
||||
*/
|
||||
this.getAddr(aAddr);
|
||||
/*
|
||||
* The physical address for any symbol located in the top 64Kb of the machine's address space
|
||||
* should be relocated to the top 64Kb of the first 1Mb, so that we're immune from any changes
|
||||
* to the A20 line.
|
||||
*/
|
||||
if ((aAddr[2] & ~0xffff) == (this.bus.busLimit & ~0xffff)) {
|
||||
aAddr[2] &= 0x000fffff;
|
||||
}
|
||||
symbol['p'] = aAddr[2];
|
||||
}
|
||||
usr.binaryInsert(aOffsetPairs, [offset, sSymbol], fnComparePairs);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1164,12 +1164,13 @@ Keyboard.prototype.resetDevice = function(fNotify)
|
|||
* TODO: There's more to reset, like LED indicators, default type rate, and emptying the scan code buffer.
|
||||
*/
|
||||
this.printMessage("keyboard reset", Messages.KEYBOARD | Messages.PORT);
|
||||
this.abScanBuffer = [Keyboard.CMDRES.BAT_OK];
|
||||
if (fNotify && this.chipset) this.chipset.notifyKbdData(this.abScanBuffer[0]);
|
||||
this.abBuffer = [Keyboard.CMDRES.BAT_OK];
|
||||
this.fAdvance = true;
|
||||
if (fNotify && this.chipset) this.chipset.notifyKbdData(this.abBuffer[0]);
|
||||
};
|
||||
|
||||
/**
|
||||
* setEnable(fData, fClock)
|
||||
* setEnabled(fData, fClock)
|
||||
*
|
||||
* This is the ChipSet's primary interface for toggling keyboard "data" and "clock" lines.
|
||||
* For MODEL_5150 and MODEL_5160 machines, this function is called from the ChipSet's PPI_B
|
||||
|
|
@ -1181,7 +1182,7 @@ Keyboard.prototype.resetDevice = function(fNotify)
|
|||
* @param {boolean} fClock is true if the keyboard's simulated clock line should be enabled
|
||||
* @return {boolean} true if keyboard was re-enabled, false if not (or no change)
|
||||
*/
|
||||
Keyboard.prototype.setEnable = function(fData, fClock)
|
||||
Keyboard.prototype.setEnabled = function(fData, fClock)
|
||||
{
|
||||
var fReset = false;
|
||||
if (this.fClock !== fClock) {
|
||||
|
|
@ -1193,6 +1194,10 @@ Keyboard.prototype.setEnable = function(fData, fClock)
|
|||
* data line is high as well.
|
||||
*/
|
||||
this.fClock = this.fResetOnEnable = fClock;
|
||||
/*
|
||||
* Allow the next buffered scan code, if any, to advance.
|
||||
*/
|
||||
if (fClock) this.fAdvance = true;
|
||||
}
|
||||
if (this.fData !== fData) {
|
||||
if (!COMPILED && this.messageEnabled(Messages.KEYBOARD | Messages.PORT)) {
|
||||
|
|
@ -1244,14 +1249,18 @@ Keyboard.prototype.sendCmd = function(bCmd)
|
|||
*
|
||||
* This is the ChipSet's interface for checking data availability.
|
||||
*
|
||||
* Note that even if we have data, we don't provide it unless fAdvance is set as well.
|
||||
* This ensures that we wait until the ROM to disable and re-enable the controller before
|
||||
* making more data available.
|
||||
*
|
||||
* @this {Keyboard}
|
||||
* @return {number} next scan code, or 0 if none
|
||||
*/
|
||||
Keyboard.prototype.checkScanCode = function()
|
||||
{
|
||||
var b = 0;
|
||||
if (this.abScanBuffer.length) {
|
||||
b = this.abScanBuffer[0];
|
||||
if (this.abBuffer.length && this.fAdvance) {
|
||||
b = this.abBuffer[0];
|
||||
if (this.chipset) this.chipset.notifyKbdData(b);
|
||||
}
|
||||
if (this.messageEnabled()) this.printMessage("scan code " + str.toHexByte(b) + " available");
|
||||
|
|
@ -1269,8 +1278,8 @@ Keyboard.prototype.checkScanCode = function()
|
|||
Keyboard.prototype.readScanCode = function()
|
||||
{
|
||||
var b = 0;
|
||||
if (this.abScanBuffer.length) {
|
||||
b = this.abScanBuffer[0];
|
||||
if (this.abBuffer.length) {
|
||||
b = this.abBuffer[0];
|
||||
}
|
||||
if (this.messageEnabled()) this.printMessage("scan code " + str.toHexByte(b) + " delivered");
|
||||
return b;
|
||||
|
|
@ -1285,7 +1294,7 @@ Keyboard.prototype.readScanCode = function()
|
|||
*/
|
||||
Keyboard.prototype.flushScanCode = function()
|
||||
{
|
||||
this.abScanBuffer = [];
|
||||
this.abBuffer = [];
|
||||
if (this.messageEnabled()) this.printMessage("scan codes flushed");
|
||||
};
|
||||
|
||||
|
|
@ -1299,18 +1308,19 @@ Keyboard.prototype.flushScanCode = function()
|
|||
*/
|
||||
Keyboard.prototype.shiftScanCode = function(fNotify)
|
||||
{
|
||||
if (this.abScanBuffer.length > 0) {
|
||||
if (this.abBuffer.length > 0) {
|
||||
/*
|
||||
* The keyboard interrupt service routine toggles the enable bit after reading a scan code, so
|
||||
* presumably this is the proper point at which to shift the last scan code out, and then assert
|
||||
* another interrupt if more scan codes exist.
|
||||
*/
|
||||
this.abScanBuffer.shift();
|
||||
this.abBuffer.shift();
|
||||
this.fAdvance = fNotify;
|
||||
if (fNotify) {
|
||||
if (!this.abScanBuffer.length || !this.chipset) {
|
||||
if (!this.abBuffer.length || !this.chipset) {
|
||||
fNotify = false;
|
||||
} else {
|
||||
this.chipset.notifyKbdData(this.abScanBuffer[0]);
|
||||
this.chipset.notifyKbdData(this.abBuffer[0]);
|
||||
}
|
||||
}
|
||||
if (this.messageEnabled()) this.printMessage("scan codes shifted, notify " + (fNotify? "true" : "false"));
|
||||
|
|
@ -1332,7 +1342,7 @@ Keyboard.prototype.powerUp = function(data, fRepower)
|
|||
* TODO: Save/restore support for Keyboard is the barest minimum. In fact, originally, I wasn't
|
||||
* saving/restoring anything, and that was OK, but if we don't at least re-initialize fClock/fData,
|
||||
* we can get a spurious reset following a restore. In an ideal world, we might choose to save/restore
|
||||
* abScanBuffer as well, but realistically, I think it's going to be safer to always start with an
|
||||
* abBuffer as well, but realistically, I think it's going to be safer to always start with an
|
||||
* empty buffer--and who's going to notice anyway?
|
||||
*
|
||||
* So, like Debugger, we deviate from the typical save/restore pattern: instead of reset OR restore,
|
||||
|
|
@ -1377,9 +1387,10 @@ Keyboard.prototype.reset = function()
|
|||
this.bitsState = this.bitsStateSim = 0;
|
||||
|
||||
/*
|
||||
* New scan codes are "pushed" onto abScanBuffer and then "shifted" off.
|
||||
* New scan codes are "pushed" onto abBuffer and then "shifted" off.
|
||||
*/
|
||||
this.abScanBuffer = [];
|
||||
this.abBuffer = [];
|
||||
this.fAdvance = true;
|
||||
|
||||
this.prevCharDown = 0;
|
||||
this.prevKeyDown = 0;
|
||||
|
|
@ -1430,7 +1441,7 @@ Keyboard.prototype.initState = function(data)
|
|||
{
|
||||
var i = 0;
|
||||
if (data === undefined) data = [];
|
||||
this.fClock = data[i++];
|
||||
this.fClock = this.fAdvance = data[i++];
|
||||
this.fData = data[i];
|
||||
return true;
|
||||
};
|
||||
|
|
@ -1478,17 +1489,17 @@ Keyboard.prototype.addScanCode = function(bScan)
|
|||
* or if we need to protect other methods from prematurely accessing certain Keyboard structures,
|
||||
* as a result of calls from any of the key event handlers established by setBinding().
|
||||
*/
|
||||
if (this.abScanBuffer) {
|
||||
if (this.abScanBuffer.length < Keyboard.LIMIT.MAX_SCANCODES) {
|
||||
if (this.abBuffer) {
|
||||
if (this.abBuffer.length < Keyboard.LIMIT.MAX_SCANCODES) {
|
||||
if (this.messageEnabled()) this.printMessage("scan code " + str.toHexByte(bScan) + " buffered");
|
||||
this.abScanBuffer.push(bScan);
|
||||
if (this.abScanBuffer.length == 1) {
|
||||
this.abBuffer.push(bScan);
|
||||
if (this.abBuffer.length == 1) {
|
||||
if (this.chipset) this.chipset.notifyKbdData(bScan);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this.abScanBuffer.length == Keyboard.LIMIT.MAX_SCANCODES) {
|
||||
this.abScanBuffer.push(Keyboard.CMDRES.BUFF_FULL);
|
||||
if (this.abBuffer.length == Keyboard.LIMIT.MAX_SCANCODES) {
|
||||
this.abBuffer.push(Keyboard.CMDRES.BUFF_FULL);
|
||||
}
|
||||
this.printMessage("scan code buffer overflow");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,6 +221,9 @@ pre a, code a {
|
|||
.md-list li p {
|
||||
padding-left: 2em;
|
||||
}
|
||||
.md-list li p br {
|
||||
line-height: 3em;
|
||||
}
|
||||
.md-list-compact {
|
||||
}
|
||||
.md-list-compact li {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ h4 a {
|
|||
p {
|
||||
line-height: 1.5em;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
a img {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
|
@ -212,6 +215,9 @@ pre a, code a {
|
|||
.md-list li p {
|
||||
padding-left: 2em;
|
||||
}
|
||||
.md-list li p br {
|
||||
line-height: 3em;
|
||||
}
|
||||
.md-list-compact {
|
||||
}
|
||||
.md-list-compact li {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ h4 a {
|
|||
p {
|
||||
line-height: 1.5em;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
a img {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
|
@ -212,6 +215,9 @@ pre a, code a {
|
|||
.md-list li p {
|
||||
padding-left: 2em;
|
||||
}
|
||||
.md-list li p br {
|
||||
line-height: 3em;
|
||||
}
|
||||
.md-list-compact {
|
||||
}
|
||||
.md-list-compact li {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue