From 8d034a73a413d5f15eb532d49cd6dd185ec8418a Mon Sep 17 00:00:00 2001 From: Jeff Parsons Date: Wed, 8 Jul 2015 12:30:00 -0700 Subject: [PATCH] Don't halt on (mostly harmless) read/write mode warnings --- modules/pcjs/lib/video.js | 142 +++++++++++++++++++++++++------------- 1 file changed, 95 insertions(+), 47 deletions(-) diff --git a/modules/pcjs/lib/video.js b/modules/pcjs/lib/video.js index cbce77e72..ebb90ccdc 100644 --- a/modules/pcjs/lib/video.js +++ b/modules/pcjs/lib/video.js @@ -789,7 +789,7 @@ Video.ATTRS.DRAW_CURSOR = 0x200; // this is an internal attribute bit, in * **0xF0: blinking reverse ATTR_FGND_WHITE | ATTR_FGND_BRIGHT | ATTR_BGND_BLINK (or bright background if blink disabled) * * Unsupported attributes reportedly display as "normal" (ATTR_FGND_WHITE | ATTR_BGND_BLACK). However, precisely which - * attributes are unsupported on the MDA varies depending on the source. Some sources (eg, the IBM Tech Ref) imply that + * attributes are unsupported on the MDA varies depending on the source. Some sources (eg, the IBM Tech Ref) imply that * only those marked by * are supported, while others (eg, some--but not all--Peter Norton guides) include those marked * by **, and still others include ALL the combinations listed above. * @@ -816,7 +816,7 @@ Video.ATTRS.DRAW_CURSOR = 0x200; // this is an internal attribute bit, in */ /* - * CGA attribute byte definitions; these simply extend the set of MDA attributes, with the exception of ATTR_FNGD_ULINE, + * CGA attribute byte definitions; these simply extend the set of MDA attributes, with the exception of ATTR_FNGD_ULINE, * which the CGA can treat only as ATTR_FGND_BLUE. */ Video.ATTRS.FGND_BLUE = 0x01; @@ -833,7 +833,8 @@ Video.ATTRS.BGND_RED = 0x40; Video.ATTRS.BGND_MAGENTA = 0x50; Video.ATTRS.BGND_BROWN = 0x60; -/* For the MDA, the number of unique "colors" is 5, based on the following supported FGND attribute values: +/* + * For the MDA, the number of unique "colors" is 5, based on the following supported FGND attribute values: * * 0x0: black font (attribute value 0x8 is mapped to 0x0) * 0x1: green font with underline @@ -843,7 +844,7 @@ Video.ATTRS.BGND_BROWN = 0x60; * * I'm still not sure about 0x8 (dark green?); for now, I'm mapping it to 0x0, but it may become a 6th supported color. * - * MDA attributes form an index into aMDAColorMap, which produces an index (0-4) into aMDAColors. + * MDA attributes form an index into aMDAColorMap, which in turn provides an index (0-4) into aMDAColors. */ Video.aMDAColors = [ [0x00, 0x00, 0x00, 0xff], @@ -890,14 +891,14 @@ Video.aEGAByteToDW = [ ]; Video.aEGADWToByte = []; -Video.aEGADWToByte[0x00000000] = 0x0; -Video.aEGADWToByte[0x00000080] = 0x1; -Video.aEGADWToByte[0x00008000] = 0x2; -Video.aEGADWToByte[0x00008080] = 0x3; -Video.aEGADWToByte[0x00800000] = 0x4; -Video.aEGADWToByte[0x00800080] = 0x5; -Video.aEGADWToByte[0x00808000] = 0x6; -Video.aEGADWToByte[0x00808080] = 0x7; +Video.aEGADWToByte[0x00000000] = 0x0; +Video.aEGADWToByte[0x00000080] = 0x1; +Video.aEGADWToByte[0x00008000] = 0x2; +Video.aEGADWToByte[0x00008080] = 0x3; +Video.aEGADWToByte[0x00800000] = 0x4; +Video.aEGADWToByte[0x00800080] = 0x5; +Video.aEGADWToByte[0x00808000] = 0x6; +Video.aEGADWToByte[0x00808080] = 0x7; Video.aEGADWToByte[0x80000000|0] = 0x8; Video.aEGADWToByte[0x80000080|0] = 0x9; Video.aEGADWToByte[0x80008000|0] = 0xa; @@ -1107,10 +1108,7 @@ Card.CRTC = { INTERLACE_POS: 0x08, MAX_SCAN: { INDX: 0x09, - SCAN_LINE: 0x1f, - VBLANK_START_BIT9: 0x20, - LINE_COMPARE_BIT9: 0x40, - CONVERT400: 0x80 + MASK: 0x1F }, CURSOR_START: { INDX: 0x0A, @@ -1144,19 +1142,43 @@ Card.CRTC = { HRETRACE_END: 0x05, VTOTAL: 0x06, OVERFLOW: { - INDX: 0x07, - VTOTAL_BIT8: 0x01, // bit 8 of register 0x06 - VDISP_END_BIT8: 0x02, // bit 8 of register 0x12 - VRETRACE_START_BIT8:0x04, // bit 8 of register 0x10 - VBLANK_START_BIT8: 0x08, // bit 8 of register 0x15 - LINE_COMPARE_BIT8: 0x10, // bit 8 of register 0x18 - CURSOR_START_BIT8: 0x20, // bit 8 of register 0x0A (EGA only) - VTOTAL_BIT9: 0x20, // bit 9 of register 0x06 (VGA only) - VDISP_END_BIT9: 0x40, // bit 9 of register 0x12 (VGA only, unused on EGA) - VRETRACE_START_BIT9:0x80 // bit 9 of register 0x10 (VGA only, unused on EGA) + INDX: 0x07, + VTOTAL_BIT8: 0x01, // bit 8 of register 0x06 + VDISP_END_BIT8: 0x02, // bit 8 of register 0x12 + VRETRACE_START_BIT8:0x04, // bit 8 of register 0x10 + VBLANK_START_BIT8: 0x08, // bit 8 of register 0x15 + LINE_COMPARE_BIT8: 0x10, // bit 8 of register 0x18 + CURSOR_START_BIT8: 0x20, // bit 8 of register 0x0A (EGA only) + VTOTAL_BIT9: 0x20, // bit 9 of register 0x06 (VGA only) + VDISP_END_BIT9: 0x40, // bit 9 of register 0x12 (VGA only, unused on EGA) + VRETRACE_START_BIT9:0x80 // bit 9 of register 0x10 (VGA only, unused on EGA) }, PRESET_SCAN: 0x08, - /* EGA/VGA CRTC registers 0x09-0x0F are the same as the MDA/CGA CRTC registers defined above */ + /* + * NOTE: EGA/VGA CRTC registers 0x09-0x0F are the same as the MDA/CGA CRTC registers defined above + */ + MAX_SCAN: { + INDX: 0x09, + SCAN_LINE: 0x1f, + VBLANK_START_BIT9: 0x20, + LINE_COMPARE_BIT9: 0x40, + CONVERT400: 0x80 + }, + CURSOR_START: { + INDX: 0x0A, + MASK: 0x1F, + BLINKON: 0x00, // (supposedly, 0x04 has the same effect as 0x00) + BLINKOFF: 0x20, // if blinking is disabled, the cursor is effectively hidden + BLINKFAST: 0x60 // default is 1/16 of the frame rate; this switches to 1/32 of the frame rate + }, + CURSOR_END: { + INDX: 0x0B, + MASK: 0x1F + }, + START_ADDR_HI: 0x0C, + START_ADDR_LO: 0x0D, + CURSOR_ADDR_HI: 0x0E, + CURSOR_ADDR_LO: 0x0F, VRETRACE_START: 0x10, VRETRACE_END: 0x11, VDISP_END: 0x12, @@ -1295,8 +1317,7 @@ Card.ATC = { if (DEBUGGER) { Card.ATC.REGS = ["PAL00","PAL01","PAL02","PAL03","PAL04","PAL05","PAL06","PAL07", - "PAL08","PAL09","PAL0A","PAL0B","PAL0C","PAL0D","PAL0E","PAL0F", - "MODE","OVERSCAN","PLANES","HPAN"]; + "PAL08","PAL09","PAL0A","PAL0B","PAL0C","PAL0D","PAL0E","PAL0F", "MODE","OVERSCAN","PLANES","HPAN"]; } /* @@ -1611,12 +1632,11 @@ Card.ACCESS = { }; /* - * Table of older (V1) access values and their corresponding new values; the new values are similar but a little - * more rational (for example, using common values for all the logical operations across modes). + * Table of older (V1) access values and their corresponding new values; the new values are similar but more orthogonal */ Card.ACCESS.V1 = []; Card.ACCESS.V1[0x0002] = Card.ACCESS.READ.MODE0; -Card.ACCESS.V1[0x0003] = Card.ACCESS.READ.MODE0 | Card.ACCESS.READ.EVENODD; +Card.ACCESS.V1[0x0003] = Card.ACCESS.READ.MODE0 | Card.ACCESS.READ.EVENODD; Card.ACCESS.V1[0x0010] = Card.ACCESS.READ.MODE1; Card.ACCESS.V1[0x0200] = Card.ACCESS.WRITE.MODE0; Card.ACCESS.V1[0x0400] = Card.ACCESS.WRITE.MODE0 | Card.ACCESS.WRITE.ROT; @@ -1727,15 +1747,15 @@ Card.ACCESS.readByteMode1 = function readByteMode1(off, addr) * Supporting Set/Reset means that for every plane for which Set/Reset is enabled, we must * replace the corresponding byte in dw with a byte of zeros or ones. This is accomplished with * nSetMapMask, nSetMapData, and nSetMapBits. nSetMapMask is the inverse of the ESRESET bits, - * because we use it to mask the processor data, nSetMapData records the desired SRESET bits, and - * nSetMapBits contains the bits to replace those that we masked in the processor data. + * because we use it to mask the processor data, nSetMapData records the desired SRESET bits, + * and nSetMapBits contains the bits to replace those that we masked in the processor data. * * We could have done this: * * dw = (dw & this.controller.nSetMapMask) | (this.controller.nSetMapData & ~this.controller.nSetMapMask) * - * but by maintaining nSetMapBits equal to (nSetMapData & ~nSetMapMask), we are able to make the writes - * slightly more efficient. + * but by maintaining nSetMapBits equal to (nSetMapData & ~nSetMapMask), we are able to make + * the writes slightly more efficient. * * @this {Memory} * @param {number} off @@ -1778,11 +1798,11 @@ Card.ACCESS.writeByteMode0 = function writeByteMode0(off, b, addr) * because we would be using sequential video buffer locations, instead of multiples of 4, and would match how * pixels are stored in "Mode X". However, I don't think that's how CHAIN4 modes operate (although that still * needs to be confirmed, because multiple sources conflict on this point). TODO: Confirm CHAIN4 operation on - * actual VGA hardware, including the extent to which ALU and other writeByteMode0() operations need to be - * folded into this. + * actual VGA hardware, including the extent to which ALU and other writeByteMode0() functionality needs to + * be folded into this. * - * It probably doesn't matter that much, as long as both the read and write CHAIN4 functions decode their - * addresses in exactly the same manner; we'd only get into trouble with software that "unchained" or + * Address decoding may not matter that much, as long as both the read and write CHAIN4 functions decode their + * addresses in exactly the same manner; we'd only get into trouble with software that "unchained" or otherwise * reconfigured the planes and then made assumptions about existing data in the video buffer. * * @this {Memory} @@ -2495,7 +2515,21 @@ Card.prototype.setMemoryAccess = function(nAccess) if (!fnReadByte) { if (DEBUG && this.dbg) { this.dbg.message("Card.setMemoryAccess(" + str.toHexWord(nAccess) + "): missing readByte handler"); - this.dbg.stopCPU(); // let's take a look + /* + * I've taken a look, and the cases I've seen so far stem from the order in which the IBM VGA BIOS + * reprograms registers during a mode change: it reprograms the Sequencer registers BEFORE the Graphics + * Controller registers, so if GRC.MODE was set to READ.MODE1 prior to the mode change and the new mode + * clears SEQ.MEMMODE.SEQUENTIAL, we will briefly be in an "odd" (unsupported) state. + * + * This didn't used to occur when we relied on the GRC.MODE register instead of the SEQ.MEMMODE for + * determining the EVENODD state. But, as explained in getAccess(), we've run into inconsistencies in + * how GRC.MODE.EVENODD is programmed, so we must live with this warning. + * + * The ultimate solution is to provide a EVENODD handler for READ.MODE1, since there is the remote + * possibility of third-party software that relies on that "odd" combination. + * + * this.dbg.stopCPU(); // let's take a look + */ } if (nReadAccess & Card.ACCESS.READ.EVENODD) { fnReadByte = Card.ACCESS.afn[Card.ACCESS.READ.EVENODD]; @@ -2506,7 +2540,21 @@ Card.prototype.setMemoryAccess = function(nAccess) if (!fnWriteByte) { if (DEBUG && this.dbg) { this.dbg.message("Card.setMemoryAccess(" + str.toHexWord(nAccess) + "): missing writeByte handler"); - this.dbg.stopCPU(); // let's take a look + /* + * I've taken a look, and the cases I've seen so far stem from the order in which the IBM VGA BIOS + * reprograms registers during a mode change: it reprograms the Sequencer registers BEFORE the Graphics + * Controller registers, so if GRC.MODE was set to WRITE.MODE2 prior to the mode change and the new mode + * clears SEQ.MEMMODE.SEQUENTIAL, we will briefly be in an "odd" (unsupported) state. + * + * This didn't used to occur when we relied on the GRC.MODE register instead of the SEQ.MEMMODE for + * determining the EVENODD state. But, as explained in getAccess(), we've run into inconsistencies in + * how GRC.MODE.EVENODD is programmed, so we must live with this warning. + * + * The ultimate solution is to provide EVENODD handlers for all modes other than WRITE.MODE0, since there + * is the remote possibility of third-party software that relies on one of those "odd" combinations. + * + * this.dbg.stopCPU(); // let's take a look + */ } if (nWriteAccess & Card.ACCESS.WRITE.EVENODD) { fnWriteByte = Card.ACCESS.afn[Card.ACCESS.WRITE.EVENODD]; @@ -3981,7 +4029,7 @@ Video.prototype.checkCursor = function() var bCursorFlags = this.cardActive.regCRTData[Card.CRTC.CURSOR_START.INDX]; var bCursorStart = bCursorFlags & Card.CRTC.CURSOR_START.MASK; var bCursorEnd = this.cardActive.regCRTData[Card.CRTC.CURSOR_END.INDX] & Card.CRTC.CURSOR_END.MASK; - var bCursorMax = this.cardActive.regCRTData[Card.CRTC.MAX_SCAN.INDX] & Card.CRTC.CURSOR_END.MASK; + var bCursorMax = this.cardActive.regCRTData[Card.CRTC.MAX_SCAN.INDX] & Card.CRTC.MAX_SCAN.MASK; /* * HACK: The original EGA BIOS has a cursor emulation bug when 43-line mode is enabled, so we attempt to detect @@ -4254,7 +4302,7 @@ Video.prototype.setDimensions = function() * then we'll need to load another MDA font variation, because we only load an 9x14 font for MDA. */ if (this.cardActive === this.cardEGA && this.nFont == Video.FONT.CGA) { - if (this.cardEGA.regCRTData[Card.CRTC.MAX_SCAN.INDX] == 7) { + if (this.cardEGA.regCRTData[Card.CRTC.EGA.MAX_SCAN.INDX] == 7) { /* * Vertical resolution of 350 divided by 8 (ie, scan lines 0-7) yields 43 whole rows. */ @@ -4264,7 +4312,7 @@ Video.prototype.setDimensions = function() * Since we can also be called before any hardware registers have been initialized, * it may be best to not perform the following test (which is why it's commented out). */ - else /* if (this.cardEGA.regCRTData[Card.CRTC.MAX_SCAN.INDX] == 13) */ { + else /* if (this.cardEGA.regCRTData[Card.CRTC.EGA.MAX_SCAN.INDX] == 13) */ { /* * Vertical resolution of 350 divided by 14 (ie, scan lines 0-13) yields exactly 25 rows. * @@ -4499,7 +4547,7 @@ Video.prototype.checkMode = function(fForce) * mode) and then which one (ie, 320 wide or 640 wide). */ if (card.regGRCData[Card.GRC.MODE.INDX] & Card.GRC.MODE.COLOR256) { - if (card.regCRTData[Card.CRTC.MAX_SCAN.INDX] & Card.CRTC.MAX_SCAN.SCAN_LINE) { + if (card.regCRTData[Card.CRTC.EGA.MAX_SCAN.INDX] & Card.CRTC.EGA.MAX_SCAN.SCAN_LINE) { if (card.regCRTData[Card.CRTC.EGA.VDISP_END] <= 0x8F) { nMode = Video.MODE.VGA_320X200; } @@ -5769,7 +5817,7 @@ Video.prototype.inSEQData = function(port, addrFrom) { var b = this.cardEGA.regSEQData[this.cardEGA.regSEQIndx]; if (!addrFrom || this.messageEnabled()) { - this.printMessageIO(Card.SEQ.DATA.PORT, null, addrFrom, "SEQ" + this.cardEGA.asSEQRegs[this.cardEGA.regSEQIndx], b); + this.printMessageIO(Card.SEQ.DATA.PORT, null, addrFrom, "SEQ." + this.cardEGA.asSEQRegs[this.cardEGA.regSEQIndx], b); } return b; };