Black Book demo L23-1 works now

This commit is contained in:
Jeff Parsons 2015-06-19 11:01:51 -07:00
commit ef3873da6e
4 changed files with 158 additions and 60 deletions

View file

@ -3099,8 +3099,21 @@ if (DEBUGGER) {
*/
var fBreak = false;
if (!this.nBreakSuppress++) {
addr = this.mapBreakpoint(addr);
for (var i = 1; i < aBreak.length; i++) {
/*
* As discussed in opINT3(), I decided to check for INT3 instructions here: we'll tell the CPU to
* stop on INT3 whenever both the INT and HALT message bits are set; a simple "g" command allows you
* to continue.
*/
if (this.messageEnabled(Messages.INT | Messages.HALT)) {
if (this.cpu.probeAddr(addr) == X86.OPCODE.INT3) {
fBreak = true;
}
}
for (var i = 1; !fBreak && i < aBreak.length; i++) {
var dbgAddrBreak = aBreak[i];
@ -3129,7 +3142,6 @@ if (DEBUGGER) {
this.println("breakpoint hit: " + this.hexAddr(dbgAddrBreak) + " (" + aBreak[0] + ")");
}
fBreak = true;
break;
}
}
}

View file

@ -975,11 +975,11 @@ function Card(video, iCard, data, cbMemory)
var monitorSpecs = Video.monitorSpecs[nMonitorType] || Video.monitorSpecs[ChipSet.MONITOR.MONO];
var nCyclesPerSecond = video.cpu.getCyclesPerSecond(); // eg, 4772727
this.nCyclesHorzPeriod = (nCyclesPerSecond / monitorSpecs.nHorzPeriodsPerSec) | 0;
this.nCyclesHorzActive = (this.nCyclesHorzPeriod * monitorSpecs.percentHorzActive / 100) | 0;
this.nCyclesVertPeriod = this.nCyclesHorzPeriod * monitorSpecs.nHorzPeriodsPerFrame;
this.nCyclesVertActive = (this.nCyclesVertPeriod * monitorSpecs.percentVertActive / 100) | 0;
this.nInitCycles = (data[7] == null? 0 : data[7]);
this.nCyclesHorzPeriod = (nCyclesPerSecond / monitorSpecs.nHorzPeriodsPerSec)|0;
this.nCyclesHorzActive = (this.nCyclesHorzPeriod * monitorSpecs.percentHorzActive / 100)|0;
this.nCyclesVertPeriod = (this.nCyclesHorzPeriod * monitorSpecs.nHorzPeriodsPerFrame)|0;
this.nCyclesVertActive = (this.nCyclesVertPeriod * monitorSpecs.percentVertActive / 100)|0;
this.nInitCycles = (data[7] || 0);
}
}
@ -2031,12 +2031,13 @@ Card.prototype.initEGA = function(data, nMonitorType)
/*22*/ 0,
/*23*/ 0,
/*24*/ 0,
/*25*/ Card.VGA_ENABLE.ENABLED,
/*26*/ Card.DAC.MASK.DEFAULT,
/*27*/ 0,
/*25*/ 0,
/*26*/ Card.VGA_ENABLE.ENABLED,
/*27*/ Card.DAC.MASK.DEFAULT,
/*28*/ 0,
/*29*/ Card.DAC.STATE.MODE_WRITE,
/*30*/ new Array(Card.DAC.TOTAL_REGS)
/*29*/ 0,
/*30*/ Card.DAC.STATE.MODE_WRITE,
/*31*/ new Array(Card.DAC.TOTAL_REGS)
];
}
@ -2112,14 +2113,15 @@ Card.prototype.initEGA = function(data, nMonitorType)
this.nSetMapBits = data[22];
this.nColorCompare = data[23];
this.nColorDontCare = data[24];
this.nStartAddress = data[25]; // this is the last CRTC start address latched from CRTC.START_ADDR_HI,CRTC.START_ADDR_LO
if (this.nCard == Video.CARD.VGA) {
this.regVGAEnable = data[25];
this.regDACMask = data[26];
this.regDACAddr = data[27];
this.regDACShift = data[28];
this.regDACState = data[29];
this.regDACData = data[30];
this.regVGAEnable = data[26];
this.regDACMask = data[27];
this.regDACAddr = data[28];
this.regDACShift = data[29];
this.regDACState = data[30];
this.regDACData = data[31];
}
};
@ -2181,14 +2183,15 @@ Card.prototype.saveEGA = function()
data[22] = this.nSetMapBits;
data[23] = this.nColorCompare;
data[24] = this.nColorDontCare;
data[25] = this.nStartAddress;
if (this.nCard == Video.CARD.VGA) {
data[25] = this.regVGAEnable;
data[26] = this.regDACMask;
data[27] = this.regDACAddr;
data[28] = this.regDACShift;
data[29] = this.regDACState;
data[30] = this.regDACData;
data[26] = this.regVGAEnable;
data[27] = this.regDACMask;
data[28] = this.regDACAddr;
data[29] = this.regDACShift;
data[30] = this.regDACState;
data[31] = this.regDACData;
}
return data;
};
@ -4625,12 +4628,14 @@ Video.prototype.updateScreen = function(fForce)
* unless fForce is set.
*/
var fEnabled = false;
if (this.cardActive) {
if (this.cardActive === this.cardEGA) {
if (this.cardEGA.regATCIndx & Card.ATC.INDX_PAL_ENABLE) fEnabled = true;
var card = this.cardActive;
if (card) {
if (card !== this.cardEGA) {
if (card.regMode & Card.CGA.MODE.VIDEO_ENABLE) fEnabled = true;
}
else {
if (this.cardActive.regMode & Card.CGA.MODE.VIDEO_ENABLE) fEnabled = true;
if (card.regATCIndx & Card.ATC.INDX_PAL_ENABLE) fEnabled = true;
}
}
@ -4668,9 +4673,25 @@ Video.prototype.updateScreen = function(fForce)
* to follow. FYI, in these calculations, offScreen does not refer to "off-screen" memory,
* but rather the "offset" of the start of visible screen memory.
*/
var addrScreen = this.cardActive.addrBuffer;
var addrScreenLimit = addrScreen + this.cardActive.sizeBuffer;
var offScreen = ((this.cardActive.regCRTData[Card.CRTC.START_ADDR_HI] << 8) + this.cardActive.regCRTData[Card.CRTC.START_ADDR_LO])|0;
var addrScreen = card.addrBuffer;
var addrScreenLimit = addrScreen + card.sizeBuffer;
/*
* HACK: nStartAddress is supposed to be "latched" ONLY at the start of every VERT_RETRACE interval;
* this is an attempt to honor that behavior, but unfortunately, updateScreen() is currently called at
* the CPU's discretion, not necessarily in sync with nCyclesVertPeriod. As a result, we must rely
* on other "triggers" to update our latched CRTC start address (eg, see outATC()).
*
* TODO: Consider matching the CPU's nCyclesNextVideoUpdate to the card's nCyclesVertPeriod, ensuring
* that CPU bursts are in sync with VERT_RETRACE. Note, however, that that will be complicated by other
* factors, such as the horizontal retrace interval, and the timing requirements of other cards in a
* multi-display configuration.
*/
if (this.getRetraceBits(card) & Card.CGA.STATUS.VERT_RETRACE) {
card.nStartAddress = ((card.regCRTData[Card.CRTC.START_ADDR_HI] << 8) + card.regCRTData[Card.CRTC.START_ADDR_LO])|0;
}
var offScreen = card.nStartAddress;
/*
* Any screen (aka "page") offset must be doubled for text modes, due to the attribute bytes.
@ -4682,14 +4703,14 @@ Video.prototype.updateScreen = function(fForce)
addrScreen += offScreen;
var cbScreen = this.cbScreen;
if (this.nCard >= Video.CARD.EGA && this.cardActive.regCRTData[Card.CRTC.EGA.OFFSET]) {
if (this.nCard >= Video.CARD.EGA && card.regCRTData[Card.CRTC.EGA.OFFSET]) {
/*
* Pre-EGA, the extent of visible screen memory (cbScreen) was derived from nCols * nRows, but since
* then, the logical width of screen memory (nColsLogical) can differ from the visible width (nCols).
* We now calculate the logical width, and the compute a new cbScreen in much the same way the original
* cbScreen was computed (but without any CGA-related padding considerations).
*/
this.nColsLogical = this.cardActive.regCRTData[Card.CRTC.EGA.OFFSET] << (this.nFont? 1 : 4);
this.nColsLogical = card.regCRTData[Card.CRTC.EGA.OFFSET] << (this.nFont? 1 : 4);
cbScreen = ((((this.nColsLogical * (this.nRows-1) + this.nCols) / this.nCellsPerWord) << 1) + this.cbPadding)|0;
}
@ -5010,6 +5031,48 @@ Video.prototype.updateScreenGraphicsEGA = function(addrScreen, addrScreenLimit)
}
};
/**
* getRetraceBits(card)
*
* This returns a byte value with two bits set or clear as appropriate: DISP_RETRACE and VERT_RETRACE.
*
* @this {Video}
* @param {Object} card
* @return {number}
*/
Video.prototype.getRetraceBits = function(card)
{
var b = 0;
/*
* NOTE: The CGA bits CGA.STATUS.DISP_RETRACE (0x01) and CGA.STATUS.VERT_RETRACE (0x08) match the EGA definitions,
* and they also correspond to the MDA bits MDA.STATUS.HDRIVE (0x01) and MDA.STATUS.BWVIDEO (0x08); I'm not sure why
* the MDA uses different designations, but the bits appear to serve the same purpose.
*
* TODO: Decide whether this more faithful emulation of the retrace bits should be extended to the MDA/CGA, too;
* doing so might slow down the BIOS scroll code a bit, though.
*/
var nCycles = this.cpu.getCycles();
var nElapsedCycles = nCycles - card.nInitCycles;
if (nElapsedCycles < 0) nElapsedCycles = 0; // TODO: Determine if this ever happens
var nCyclesHorzRemain = nElapsedCycles % card.nCyclesHorzPeriod;
if (nCyclesHorzRemain > card.nCyclesHorzActive) b |= Card.CGA.STATUS.DISP_RETRACE;
var nCyclesVertRemain = nElapsedCycles % card.nCyclesVertPeriod;
if (nCyclesVertRemain > card.nCyclesVertActive) b |= Card.CGA.STATUS.VERT_RETRACE | Card.CGA.STATUS.DISP_RETRACE;
/*
* This is optional: the number of CPU cycles that remain in the current vertical period is all we need to keep
* track of (the number of cycles since the card was initialized is fine, too, but that delta can become extremely
* large after a while).
*
* card.nInitCycles = nCycles - nCyclesVertRemain;
*
* NOTE: Now that we're calling getRetraceBits() more frequently (ie, for internal checks), resetting nInitCycles
* in this fashion preserves the vertical period at the expense of the horizontal period, which in turn can cause
* grief in ROM BIOS code that requires strict horizontal retrace times. So the above code is now disabled.
*/
return b;
};
/**
* inMDAIndx(port, addrFrom)
*
@ -5162,6 +5225,11 @@ Video.prototype.outATC = function(port, bOut, addrFrom)
this.updateScreen(true);
}
}
/*
* HACK: nStartAddress is supposed to be "latched" ONLY at the start of every VERT_RETRACE interval,
* but other "triggers" are currently required; see updateScreen() for details.
*/
this.cardEGA.nStartAddress = ((this.cardEGA.regCRTData[Card.CRTC.START_ADDR_HI] << 8) + this.cardEGA.regCRTData[Card.CRTC.START_ADDR_LO])|0;
} else {
var iReg = this.cardEGA.regATCIndx & Card.ATC.INDX_MASK;
if (iReg >= Card.ATC.PALETTE_REGS || !fPalEnabled) {
@ -5873,6 +5941,15 @@ Video.prototype.outCRTCData = function(card, port, bOut, addrFrom)
}
card.regCRTData[card.regCRTIndx] = bOut;
}
if (card.regCRTIndx == Card.CRTC.START_ADDR_HI || card.regCRTIndx == Card.CRTC.START_ADDR_LO) {
/*
* HACK: nStartAddress is supposed to be "latched" ONLY at the start of every VERT_RETRACE interval,
* but the best we can currently do is latch it during retrace, as well as other times (eg, see outATC()).
*/
if (this.getRetraceBits(card) & Card.CGA.STATUS.DISP_RETRACE) {
card.nStartAddress = ((card.regCRTData[Card.CRTC.START_ADDR_HI] << 8) + card.regCRTData[Card.CRTC.START_ADDR_LO])|0;
}
}
/*
* During mode changes on the EGA, all the CRTC regs are typically programmed in sequence,
* and if that's all that's happening with Card.CRTC.MAX_SCAN_LINE, then we don't want to treat
@ -5938,29 +6015,7 @@ Video.prototype.outCardMode = function(card, bOut, addrFrom)
*/
Video.prototype.inCardStatus = function(card, addrFrom)
{
var b = 0;
/*
* NOTE: The CGA bits CGA.STATUS.DISP_RETRACE (0x01) and CGA.STATUS.VERT_RETRACE (0x08) match the EGA definitions,
* and they also correspond to the MDA bits MDA.STATUS.HDRIVE (0x01) and MDA.STATUS.BWVIDEO (0x08); I'm not sure why
* the MDA uses different designations, but the bits appear to serve the same purpose.
*
* TODO: Decide whether this more faithful emulation of the retrace bits should be extended to the MDA/CGA, too;
* doing so might slow down the BIOS scroll code a bit, though.
*/
var nCycles = this.cpu.getCycles();
var nElapsedCycles = nCycles - card.nInitCycles;
if (nElapsedCycles < 0) nElapsedCycles = 0; // TODO: Determine if this ever happens
var nCyclesHorzRemain = nElapsedCycles % card.nCyclesHorzPeriod;
if (nCyclesHorzRemain > card.nCyclesHorzActive) b |= Card.CGA.STATUS.DISP_RETRACE;
var nCyclesVertRemain = nElapsedCycles % card.nCyclesVertPeriod;
if (nCyclesVertRemain > card.nCyclesVertActive) b |= Card.CGA.STATUS.VERT_RETRACE;
/*
* This is optional: the number of CPU cycles that remain in the current vertical period is all we need to keep
* track of (the number of cycles since the card was initialized is fine, too, but that delta can become extremely
* large after a while).
*/
card.nInitCycles = nCycles - nCyclesVertRemain;
var b = this.getRetraceBits(card);
if (card === this.cardEGA) {
/*
@ -6001,9 +6056,13 @@ Video.prototype.inCardStatus = function(card, addrFrom)
*
* Also, according to http://www.seasip.info/VintagePC/mda.html, on an MDA, bits 7-4 are always ON and
* bits 2-1 are always OFF, hence the "OR" of 0xf0.
*
* TODO: Decide whether to preserve the bits from getRetraceBits() on the MDA/CGA; we're continuing
* to do a simple toggle, partly on the theory that that may speed up the CGA BIOS scroll code a bit.
*/
b = (card.regStatus ^= (Card.CGA.STATUS.DISP_RETRACE | Card.CGA.STATUS.VERT_RETRACE)) | 0xf0;
}
card.regStatus = b;
this.printMessageIO(card.port + 6, null, addrFrom, (card === this.cardEGA? "STATUS1" : "STATUS"), b);
return b;

View file

@ -3576,7 +3576,7 @@ X86.fnSrcNone = function SrcNone()
};
/**
* fnFault(nFault, nError, fHalt)
* fnFault(nFault, nError, fHalt, nCycles)
*
* Helper to dispatch faults.
*
@ -3584,8 +3584,9 @@ X86.fnSrcNone = function SrcNone()
* @param {number} nFault
* @param {number} [nError] (if omitted, no error code will be pushed)
* @param {boolean} [fHalt] will halt the CPU if true *and* a Debugger is loaded
* @param {number} [nCycles] cycle count to pass through to fnINT(), if any
*/
X86.fnFault = function(nFault, nError, fHalt)
X86.fnFault = function(nFault, nError, fHalt, nCycles)
{
if (!this.aFlags.fComplete) {
this.printMessage("Fault " + str.toHexByte(nFault) + " blocked by Debugger", Messages.WARN);
@ -3623,7 +3624,7 @@ X86.fnFault = function(nFault, nError, fHalt)
fDispatch = false;
}
if (fDispatch) X86.fnINT.call(this, this.nFault = nFault, nError, 0);
if (fDispatch) X86.fnINT.call(this, this.nFault = nFault, nError, nCycles || 0);
/*
* Since this fault is likely being issued in the context of an instruction that hasn't finished
@ -3696,7 +3697,7 @@ X86.fnFaultMessage = function(nFault, nError, fHalt)
*
* When a triple fault shows up, nFault is -1; it displays as 0xff only because we use toHexByte().
*/
if (bOpcode == X86.OPCODE.INT3) {
if (bOpcode == X86.OPCODE.INT3 && !this.addrIDTLimit) {
fHalt = false;
bitsMessage |= Messages.CPU;
}

View file

@ -3322,6 +3322,32 @@ X86.opRETF = function RETF()
*/
X86.opINT3 = function INT3()
{
/*
* To give our own Debugger the ability to stop execution on INT3, I thought about treating this as
* a fault rather than an interrupt, in order to leverage the existing Debugger logic inside fnFault()
* processing, but that has the unwanted side-effect of rewinding EIP to the INT3 prior to issuing
* the interrupt, and the corresponding IRET takes us right back to the INT3.
*
* X86.fnFault.call(this, X86.EXCEPTION.BREAKPOINT, null, false, this.cycleCounts.nOpCyclesInt3D);
*
* Then I had the idea of using the fnFaultMessage() function, in much the same way that fnFault()
* does for actual faults: if the user turned on the FAULT and HALT message bits, then fnFaultMessage()
* would tell us to halt; otherwise, we'd perform the normal fnINT() call.
*
* if (X86.fnFaultMessage.call(this, X86.EXCEPTION.BREAKPOINT)) {
* this.setIP(this.opLIP - this.segCS.base);
* return;
* }
*
* However, that makes it a little tedious to get past the INT3 (you have to use a Debugger command
* like "t;g"), and a somewhat confusing fault message is displayed; eg:
*
* Fault 0x03 on opcode 0xB4 at 09CE:0155 (%009E35)
*
* The best solution was to leave this function alone, and change the Debugger's checkBreakpoint()
* function to stop execution on INT3 whenever both the INT and HALT message bits are set; a simple "g"
* command allows you to continue.
*/
X86.fnINT.call(this, X86.EXCEPTION.BREAKPOINT, null, this.cycleCounts.nOpCyclesInt3D);
};