Added support for CHAIN1
This commit is contained in:
parent
5b29529276
commit
eebf309cc4
1 changed files with 183 additions and 90 deletions
|
|
@ -363,17 +363,17 @@ Video.TRAPALL = true; // monitor all I/O by default (not just deltas)
|
||||||
* This is due to the physical design of the chip. These timings can be guaranteed by ensuring that the
|
* This is due to the physical design of the chip. These timings can be guaranteed by ensuring that the
|
||||||
* rules listed below are followed when programming the CRTC.
|
* rules listed below are followed when programming the CRTC.
|
||||||
*
|
*
|
||||||
* 1. The Horizontal Total [HORZ_TOTAL] register (R0) must be greater than or equal to a value of
|
* 1. The Horizontal Total [HTOTAL] register (R0) must be greater than or equal to a value of
|
||||||
* 25 decimal.
|
* 25 decimal.
|
||||||
*
|
*
|
||||||
* 2. The minimum positive pulse width of the HSYNC output must be four character clock units.
|
* 2. The minimum positive pulse width of the HSYNC output must be four character clock units.
|
||||||
*
|
*
|
||||||
* 3. Register R5, Horizontal Sync End [HORZ_RETRACE_END], must be programmed such that the HSYNC
|
* 3. Register R5, Horizontal Sync End [HRETRACE_END], must be programmed such that the HSYNC
|
||||||
* output goes to a logic 0 a minimum of one character clock time before the 'horizontal display enable'
|
* output goes to a logic 0 a minimum of one character clock time before the 'horizontal display enable'
|
||||||
* signal goes to a logical 1.
|
* signal goes to a logical 1.
|
||||||
*
|
*
|
||||||
* 4. Register R16, Vsync Start [VERT_RETRACE_START], must be a minimum of one horizontal scan line greater
|
* 4. Register R16, Vsync Start [VRETRACE_START], must be a minimum of one horizontal scan line greater
|
||||||
* than register R18 [VERT_DISP_END]. Register R18 defines where the 'vertical display enable' signal ends.
|
* than register R18 [VDISP_END]. Register R18 defines where the 'vertical display enable' signal ends.
|
||||||
*
|
*
|
||||||
* When bit 5 of the Attribute Mode Control register equals 1, a successful line compare (see Line Compare
|
* When bit 5 of the Attribute Mode Control register equals 1, a successful line compare (see Line Compare
|
||||||
* [LINE_COMPARE] register) in the CRT Controller forces the output of the PEL Panning register to 0's until Vsync
|
* [LINE_COMPARE] register) in the CRT Controller forces the output of the PEL Panning register to 0's until Vsync
|
||||||
|
|
@ -514,6 +514,7 @@ Video.MODE = {
|
||||||
VGA_640X480_MONO: 0x11, // mapped at A000:0000, monochrome
|
VGA_640X480_MONO: 0x11, // mapped at A000:0000, monochrome
|
||||||
VGA_640X480: 0x12, // mapped at A000:0000, color
|
VGA_640X480: 0x12, // mapped at A000:0000, color
|
||||||
VGA_320X200: 0x13, // mapped at A000:0000, color
|
VGA_320X200: 0x13, // mapped at A000:0000, color
|
||||||
|
VGA_320X400: 0x26,
|
||||||
UNKNOWN: 0xFF
|
UNKNOWN: 0xFF
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -733,6 +734,7 @@ Video.aModeParms[Video.MODE.EGA_640X350] = [640, 350, 16];
|
||||||
Video.aModeParms[Video.MODE.VGA_640X480_MONO] = [640, 480, 16]; // 0x11
|
Video.aModeParms[Video.MODE.VGA_640X480_MONO] = [640, 480, 16]; // 0x11
|
||||||
Video.aModeParms[Video.MODE.VGA_640X480] = [640, 480, 16]; // 0x12
|
Video.aModeParms[Video.MODE.VGA_640X480] = [640, 480, 16]; // 0x12
|
||||||
Video.aModeParms[Video.MODE.VGA_320X200] = [320, 200, 2]; // 0x13
|
Video.aModeParms[Video.MODE.VGA_320X200] = [320, 200, 2]; // 0x13
|
||||||
|
Video.aModeParms[Video.MODE.VGA_320X400] = [320, 400, 16]; // 0x26
|
||||||
|
|
||||||
Video.aModeParms[Video.MODE.CGA_40X25_BW] = Video.aModeParms[Video.MODE.CGA_40X25]; // 0x01
|
Video.aModeParms[Video.MODE.CGA_40X25_BW] = Video.aModeParms[Video.MODE.CGA_40X25]; // 0x01
|
||||||
Video.aModeParms[Video.MODE.CGA_80X25_BW] = Video.aModeParms[Video.MODE.CGA_80X25]; // 0x03
|
Video.aModeParms[Video.MODE.CGA_80X25_BW] = Video.aModeParms[Video.MODE.CGA_80X25]; // 0x03
|
||||||
|
|
@ -1059,10 +1061,10 @@ Card.CGA = {
|
||||||
},
|
},
|
||||||
STATUS: {
|
STATUS: {
|
||||||
PORT: 0x3DA, // read-only; same for EGA (although the EGA calls this STATUS1, to distinguish it from STATUS0)
|
PORT: 0x3DA, // read-only; same for EGA (although the EGA calls this STATUS1, to distinguish it from STATUS0)
|
||||||
DISP_RETRACE: 0x01,
|
RETRACE: 0x01,
|
||||||
PEN_TRIGGER: 0x02,
|
PEN_TRIGGER: 0x02,
|
||||||
PEN_ON: 0x04,
|
PEN_ON: 0x04,
|
||||||
VERT_RETRACE: 0x08 // when set, this indicates the CGA is performing a vertical retrace
|
VRETRACE: 0x08 // when set, this indicates the CGA is performing a vertical retrace
|
||||||
},
|
},
|
||||||
/*
|
/*
|
||||||
* TODO: Add support for light pen port(s) someday....
|
* TODO: Add support for light pen port(s) someday....
|
||||||
|
|
@ -1088,16 +1090,22 @@ Card.CGA = {
|
||||||
* looked into it yet.
|
* looked into it yet.
|
||||||
*/
|
*/
|
||||||
Card.CRTC = {
|
Card.CRTC = {
|
||||||
HORZ_TOTAL: 0x00,
|
HTOTAL: 0x00,
|
||||||
HORZ_DISP: 0x01,
|
HDISP: 0x01,
|
||||||
HORZ_SYNC_POS: 0x02,
|
HSYNC_POS: 0x02,
|
||||||
HORZ_SYNC_WIDTH: 0x03,
|
HSYNC_WIDTH: 0x03,
|
||||||
VERT_TOTAL: 0x04,
|
VTOTAL: 0x04,
|
||||||
VERT_TOTAL_ADJ: 0x05,
|
VTOTAL_ADJ: 0x05,
|
||||||
VERT_DISP_TOTAL: 0x06,
|
VDISP_TOTAL: 0x06,
|
||||||
VERT_SYNC_POS: 0x07,
|
VSYNC_POS: 0x07,
|
||||||
INTERLACE_POS: 0x08,
|
INTERLACE_POS: 0x08,
|
||||||
MAX_SCAN_LINE: 0x09,
|
MAX_SCAN: {
|
||||||
|
INDX: 0x09,
|
||||||
|
SCAN_LINE: 0x1f,
|
||||||
|
VBLANK_START_BIT9: 0x20,
|
||||||
|
LINE_COMPARE_BIT9: 0x40,
|
||||||
|
CONVERT400: 0x80
|
||||||
|
},
|
||||||
CURSOR_START: {
|
CURSOR_START: {
|
||||||
INDX: 0x0A,
|
INDX: 0x0A,
|
||||||
MASK: 0x1F,
|
MASK: 0x1F,
|
||||||
|
|
@ -1123,29 +1131,29 @@ Card.CRTC = {
|
||||||
LIGHT_PEN_LO: 0x11,
|
LIGHT_PEN_LO: 0x11,
|
||||||
TOTAL_REGS: 0x12, // total CRT registers on MDA/CGA
|
TOTAL_REGS: 0x12, // total CRT registers on MDA/CGA
|
||||||
EGA: {
|
EGA: {
|
||||||
HORZ_DISP_END: 0x01,
|
HDISP_END: 0x01,
|
||||||
HORZ_BLANK_START: 0x02,
|
HBLANK_START: 0x02,
|
||||||
HORZ_BLANK_END: 0x03,
|
HBLANK_END: 0x03,
|
||||||
HORZ_RETRACE_START: 0x04,
|
HRETRACE_START: 0x04,
|
||||||
HORZ_RETRACE_END: 0x05,
|
HRETRACE_END: 0x05,
|
||||||
VERT_TOTAL: 0x06,
|
VTOTAL: 0x06,
|
||||||
OVERFLOW: {
|
OVERFLOW: {
|
||||||
INDX: 0x07,
|
INDX: 0x07,
|
||||||
VERT_TOTAL_BIT8: 0x01, // bit 8 of register 0x06
|
VTOTAL_BIT8: 0x01, // bit 8 of register 0x06
|
||||||
VERT_DISP_END_BIT8: 0x02, // bit 8 of register 0x12
|
VDISP_END_BIT8: 0x02, // bit 8 of register 0x12
|
||||||
VERT_RETRACE_START_BIT8:0x04, // bit 8 of register 0x10
|
VRETRACE_START_BIT8:0x04, // bit 8 of register 0x10
|
||||||
VERT_BLANK_START_BIT8: 0x08, // bit 8 of register 0x15
|
VBLANK_START_BIT8: 0x08, // bit 8 of register 0x15
|
||||||
LINE_COMPARE_BIT8: 0x10, // bit 8 of register 0x18
|
LINE_COMPARE_BIT8: 0x10, // bit 8 of register 0x18
|
||||||
CURSOR_START_BIT8: 0x20, // bit 8 of register 0x0A (EGA only)
|
CURSOR_START_BIT8: 0x20, // bit 8 of register 0x0A (EGA only)
|
||||||
VERT_TOTAL_BIT9: 0x20, // bit 9 of register 0x06 (VGA only)
|
VTOTAL_BIT9: 0x20, // bit 9 of register 0x06 (VGA only)
|
||||||
VERT_DISP_END_BIT9: 0x40, // bit 9 of register 0x12 (VGA only, unused on EGA)
|
VDISP_END_BIT9: 0x40, // bit 9 of register 0x12 (VGA only, unused on EGA)
|
||||||
VERT_RETRACE_START_BIT9:0x80 // bit 9 of register 0x10 (VGA only, unused on EGA)
|
VRETRACE_START_BIT9:0x80 // bit 9 of register 0x10 (VGA only, unused on EGA)
|
||||||
},
|
},
|
||||||
PRESET_ROW_SCAN: 0x08,
|
PRESET_SCAN: 0x08,
|
||||||
/* EGA/VGA CRTC registers 0x09-0x0F are the same as the MDA/CGA CRTC registers defined above */
|
/* EGA/VGA CRTC registers 0x09-0x0F are the same as the MDA/CGA CRTC registers defined above */
|
||||||
VERT_RETRACE_START: 0x10,
|
VRETRACE_START: 0x10,
|
||||||
VERT_RETRACE_END: 0x11,
|
VRETRACE_END: 0x11,
|
||||||
VERT_DISP_END: 0x12,
|
VDISP_END: 0x12,
|
||||||
/*
|
/*
|
||||||
* The OFFSET register (bits 0-7) specifies the logical line width of the screen. The starting memory address
|
* The OFFSET register (bits 0-7) specifies the logical line width of the screen. The starting memory address
|
||||||
* for the next character row is larger than the current character row by two or four times this amount.
|
* for the next character row is larger than the current character row by two or four times this amount.
|
||||||
|
|
@ -1159,8 +1167,8 @@ Card.CRTC = {
|
||||||
COUNTBY4: 0x20,
|
COUNTBY4: 0x20,
|
||||||
DWORD: 0x40
|
DWORD: 0x40
|
||||||
},
|
},
|
||||||
VERT_BLANK_START: 0x15,
|
VBLANK_START: 0x15,
|
||||||
VERT_BLANK_END: 0x16,
|
VBLANK_END: 0x16,
|
||||||
MODE_CTRL: {
|
MODE_CTRL: {
|
||||||
INDX: 0x17,
|
INDX: 0x17,
|
||||||
CMS: 0x01, // Compatibility Mode Support (CGA A13 control)
|
CMS: 0x01, // Compatibility Mode Support (CGA A13 control)
|
||||||
|
|
@ -1179,14 +1187,14 @@ Card.CRTC = {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (DEBUGGER) {
|
if (DEBUGGER) {
|
||||||
Card.CRTC.REGS = ["HORZ_TOTAL","HORZ_DISP","HORZ_SYNC_POS","HORZ_SYNC_WIDTH","VERT_TOTAL","VERT_TOTAL_ADJ",
|
Card.CRTC.REGS = ["HTOTAL","HDISP","HSYNC_POS","HSYNC_WIDTH","VTOTAL","VTOTAL_ADJ",
|
||||||
"VERT_DISP","VERT_SYNC_POS","INTERLACE_POS","MAX_SCAN_LINE","CURSOR_START","CURSOR_END",
|
"VDISP","VSYNC_POS","INTERLACE_POS","MAX_SCAN","CURSOR_START","CURSOR_END",
|
||||||
"START_ADDR_HI","START_ADDR_LO","CURSOR_ADDR_HI","CURSOR_ADDR_LO","LIGHT_PEN_HI","LIGHT_PEN_LO"];
|
"START_ADDR_HI","START_ADDR_LO","CURSOR_ADDR_HI","CURSOR_ADDR_LO","LIGHT_PEN_HI","LIGHT_PEN_LO"];
|
||||||
|
|
||||||
Card.CRTC.EGA_REGS = ["HORZ_TOTAL","HORZ_DISP_END","HORZ_BLANK_START","HORZ_BLANK_END","HORZ_RETRACE_START","HORZ_RETRACE_END",
|
Card.CRTC.EGA_REGS = ["HTOTAL","HDISP_END","HBLANK_START","HBLANK_END","HRETRACE_START","HRETRACE_END",
|
||||||
"VERT_TOTAL","OVERFLOW","PRESET_ROW_SCAN","MAX_SCAN_LINE","CURSOR_START","CURSOR_END",
|
"VTOTAL","OVERFLOW","PRESET_SCAN","MAX_SCAN","CURSOR_START","CURSOR_END",
|
||||||
"START_ADDR_HI","START_ADDR_LO","CURSOR_ADDR_HI","CURSOR_ADDR_LO","VERT_RETRACE_START","VERT_RETRACE_END",
|
"START_ADDR_HI","START_ADDR_LO","CURSOR_ADDR_HI","CURSOR_ADDR_LO","VRETRACE_START","VRETRACE_END",
|
||||||
"VERT_DISP_END","OFFSET","UNDERLINE","VERT_BLANK_START","VERT_BLANK_END","MODE_CTRL","LINE_COMPARE"];
|
"VDISP_END","OFFSET","UNDERLINE","VBLANK_START","VBLANK_END","MODE_CTRL","LINE_COMPARE"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -1195,7 +1203,7 @@ if (DEBUGGER) {
|
||||||
* STATUS1 bit 0 has confusing documentation: the EGA Tech Ref says "Logical 0 indicates the CRT raster is in a
|
* STATUS1 bit 0 has confusing documentation: the EGA Tech Ref says "Logical 0 indicates the CRT raster is in a
|
||||||
* horizontal or vertical retrace interval", whereas the VGA Tech Ref says "Logical 1 indicates a horizontal or
|
* horizontal or vertical retrace interval", whereas the VGA Tech Ref says "Logical 1 indicates a horizontal or
|
||||||
* vertical retrace interval," but then clarifies: "This bit is the real-time status of the INVERTED display enable
|
* vertical retrace interval," but then clarifies: "This bit is the real-time status of the INVERTED display enable
|
||||||
* signal". So, instead of calling bit 0 DISP_ENABLE (or more precisely, DISP_ENABLE_INVERTED), it's simply DISP_RETRACE.
|
* signal". So, instead of calling bit 0 DISP_ENABLE (or more precisely, DISP_ENABLE_INVERTED), it's simply RETRACE.
|
||||||
*
|
*
|
||||||
* STATUS1 diagnostic bits 5 and 4 are set according to the Card.ATC.PLANES.MUX bits:
|
* STATUS1 diagnostic bits 5 and 4 are set according to the Card.ATC.PLANES.MUX bits:
|
||||||
*
|
*
|
||||||
|
|
@ -1208,8 +1216,8 @@ if (DEBUGGER) {
|
||||||
*/
|
*/
|
||||||
Card.STATUS1 = {
|
Card.STATUS1 = {
|
||||||
PORT: 0x3DA,
|
PORT: 0x3DA,
|
||||||
DISP_RETRACE: 0x01, // bit 0: logical OR of horizontal and vertical retrace
|
RETRACE: 0x01, // bit 0: logical OR of horizontal and vertical retrace
|
||||||
VERT_RETRACE: 0x08, // bit 3: set during vertical retrace interval
|
VRETRACE: 0x08, // bit 3: set during vertical retrace interval
|
||||||
DIAGNOSTIC: 0x30, // bits 5,4 are controlled by the Card.ATC.PLANES.MUX bits
|
DIAGNOSTIC: 0x30, // bits 5,4 are controlled by the Card.ATC.PLANES.MUX bits
|
||||||
RESERVED: 0xC6
|
RESERVED: 0xC6
|
||||||
};
|
};
|
||||||
|
|
@ -1309,8 +1317,8 @@ Card.MISC = {
|
||||||
CLOCK_SELECT: 0x0C, // 0x0: 14Mhz I/O clock, 0x4: 16Mhz on-board clock, 0x8: external clock, 0xC: unused
|
CLOCK_SELECT: 0x0C, // 0x0: 14Mhz I/O clock, 0x4: 16Mhz on-board clock, 0x8: external clock, 0xC: unused
|
||||||
DISABLE_DRV: 0x10, // 0 activates internal video drivers, 1 activates feature connector direct drive outputs
|
DISABLE_DRV: 0x10, // 0 activates internal video drivers, 1 activates feature connector direct drive outputs
|
||||||
PAGE_ODD_EVEN: 0x20, // 0 selects the low 64Kb page of video RAM for text modes, 1 selects the high page
|
PAGE_ODD_EVEN: 0x20, // 0 selects the low 64Kb page of video RAM for text modes, 1 selects the high page
|
||||||
HORZ_POLARITY: 0x40, // 0 selects positive horizontal retrace
|
HPOLARITY: 0x40, // 0 selects positive horizontal retrace
|
||||||
VERT_POLARITY: 0x80 // 0 selects positive vertical retrace
|
VPOLARITY: 0x80 // 0 selects positive vertical retrace
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -1577,6 +1585,7 @@ Card.ACCESS = {
|
||||||
MODE1: 0x0500,
|
MODE1: 0x0500,
|
||||||
EVENODD: 0x1000,
|
EVENODD: 0x1000,
|
||||||
CHAIN4: 0x4000,
|
CHAIN4: 0x4000,
|
||||||
|
CHAIN1: 0x8000,
|
||||||
MASK: 0xFF00
|
MASK: 0xFF00
|
||||||
},
|
},
|
||||||
WRITE: { // and WRITE values are designed to be OR'ed with READ values
|
WRITE: { // and WRITE values are designed to be OR'ed with READ values
|
||||||
|
|
@ -1585,14 +1594,15 @@ Card.ACCESS = {
|
||||||
MODE2: 0x0002,
|
MODE2: 0x0002,
|
||||||
MODE3: 0x0003, // VGA only
|
MODE3: 0x0003, // VGA only
|
||||||
CHAIN4: 0x0004,
|
CHAIN4: 0x0004,
|
||||||
|
CHAIN1: 0x0008,
|
||||||
EVENODD: 0x0010,
|
EVENODD: 0x0010,
|
||||||
ROT: 0x0020,
|
ROT: 0x0020,
|
||||||
AND: 0x0060,
|
AND: 0x0060,
|
||||||
OR: 0x00A0,
|
OR: 0x00A0,
|
||||||
XOR: 0x00E0,
|
XOR: 0x00E0,
|
||||||
MASK: 0x00F7 // 0xF7 ensures we strip any lingering V2 bit from the value
|
MASK: 0x00FF
|
||||||
},
|
},
|
||||||
V2: 0x0008 // this is a signature bit used ONLY to differentiate V2 access values from V1
|
V2: (0x80000000|0) // this is a signature bit used ONLY to differentiate V2 access values from V1
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -1647,6 +1657,23 @@ Card.ACCESS.readByteMode0Chain4 = function readByteMode0Chain4(off, addr)
|
||||||
return ((this.controller.latches = this.adw[idw]) >> shift) & 0xff;
|
return ((this.controller.latches = this.adw[idw]) >> shift) & 0xff;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* readByteMode0Chain1(off, addr)
|
||||||
|
*
|
||||||
|
* See writeByteMode0Chain1 for a description of how writes are distributed across planes.
|
||||||
|
*
|
||||||
|
* @this {Memory}
|
||||||
|
* @param {number} off
|
||||||
|
* @param {number} [addr]
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
Card.ACCESS.readByteMode0Chain1 = function readByteMode0Chain1(off, addr)
|
||||||
|
{
|
||||||
|
var idw = (off >> 2) + this.offset;
|
||||||
|
var shift = (off & 0x3) << 3;
|
||||||
|
return ((this.controller.latches = this.adw[idw]) >> shift) & 0xff;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* readByteMode0EvenOdd(off, addr)
|
* readByteMode0EvenOdd(off, addr)
|
||||||
*
|
*
|
||||||
|
|
@ -1743,8 +1770,60 @@ Card.ACCESS.writeByteMode0 = function writeByteMode0(off, b, addr)
|
||||||
/**
|
/**
|
||||||
* writeByteMode0Chain4(off, b, addr)
|
* writeByteMode0Chain4(off, b, addr)
|
||||||
*
|
*
|
||||||
* This is how we distribute a write of 0xff across the address space to the planes, assuming that
|
* This is how we distribute writes of 0xff across the address space to the planes (assuming that all
|
||||||
* all planes are enabled by the Sequencer's MAPMASK register (which we assume still controls access):
|
* planes are enabled by the Sequencer's MAPMASK register):
|
||||||
|
*
|
||||||
|
* off idw adw[idw]
|
||||||
|
* ------ ------ ----------
|
||||||
|
* 0x0000: 0x0000 0x000000ff
|
||||||
|
* 0x0001: 0x0000 0x0000ff00
|
||||||
|
* 0x0002: 0x0000 0x00ff0000
|
||||||
|
* 0x0003: 0x0000 0xff000000
|
||||||
|
* 0x0004: 0x0004 0x000000ff
|
||||||
|
* 0x0005: 0x0004 0x0000ff00
|
||||||
|
* 0x0006: 0x0004 0x00ff0000
|
||||||
|
* 0x0007: 0x0004 0xff000000
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* Some VGA emulations calculate the video buffer index (idw) by shifting the offset (off) right 2 bits,
|
||||||
|
* instead of simply masking off the low 2 bits, as we do here. That would be a more "pleasing" arrangement,
|
||||||
|
* because we would be using sequential video buffer locations, instead of multiples of 4; that's also how
|
||||||
|
* "Mode X" works. 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.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* reconfigured the planes and then made assumptions about existing data in the video buffer.
|
||||||
|
*
|
||||||
|
* NOTE: We do implement the alternate address decoding scheme, because that's what "Mode X" uses, but we call
|
||||||
|
* it CHAIN1 instead of CHAIN4.
|
||||||
|
*
|
||||||
|
* @this {Memory}
|
||||||
|
* @param {number} off
|
||||||
|
* @param {number} b (which should already be pre-masked to 8 bits; see Bus.prototype.setByteDirect)
|
||||||
|
* @param {number} [addr]
|
||||||
|
*/
|
||||||
|
Card.ACCESS.writeByteMode0Chain4 = function writeByteMode0Chain4(off, b, addr)
|
||||||
|
{
|
||||||
|
var idw = (off & ~0x3) + this.offset;
|
||||||
|
var shift = (off & 0x3) << 3;
|
||||||
|
/*
|
||||||
|
* TODO: Consider adding a separate "unmasked" version of this CHAIN4 write function when nSeqMapMask is -1
|
||||||
|
* (or removing nSeqMapMask from the equation altogether, if CHAIN4 is never used with any planes disabled).
|
||||||
|
*/
|
||||||
|
var dw = ((b << shift) & this.controller.nSeqMapMask) | (this.adw[idw] & ~((0xff << shift) & this.controller.nSeqMapMask));
|
||||||
|
if (this.adw[idw] != dw) {
|
||||||
|
this.adw[idw] = dw;
|
||||||
|
this.fDirty = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* writeByteMode0Chain1(off, b, addr)
|
||||||
|
*
|
||||||
|
* This is how we distribute writes of 0xff across the address space to the planes (assuming that
|
||||||
|
* all planes are enabled by the Sequencer's MAPMASK register); this is what "Mode X" uses.
|
||||||
*
|
*
|
||||||
* off idw adw[idw]
|
* off idw adw[idw]
|
||||||
* ------ ------ ----------
|
* ------ ------ ----------
|
||||||
|
|
@ -1763,13 +1842,13 @@ Card.ACCESS.writeByteMode0 = function writeByteMode0(off, b, addr)
|
||||||
* @param {number} b (which should already be pre-masked to 8 bits; see Bus.prototype.setByteDirect)
|
* @param {number} b (which should already be pre-masked to 8 bits; see Bus.prototype.setByteDirect)
|
||||||
* @param {number} [addr]
|
* @param {number} [addr]
|
||||||
*/
|
*/
|
||||||
Card.ACCESS.writeByteMode0Chain4 = function writeByteMode0Chain4(off, b, addr)
|
Card.ACCESS.writeByteMode0Chain1 = function writeByteMode0Chain1(off, b, addr)
|
||||||
{
|
{
|
||||||
var idw = (off & ~0x3) + this.offset;
|
var idw = (off >> 2) + this.offset;
|
||||||
var shift = (off & 0x3) << 3;
|
var shift = (off & 0x3) << 3;
|
||||||
/*
|
/*
|
||||||
* TODO: Consider adding a separate "unmasked" version of this CHAIN4 write function whenever nSeqMapMask is -1
|
* TODO: Consider adding a separate "unmasked" version of this CHAIN1 write function when nSeqMapMask is -1
|
||||||
* (or removing nSeqMapMask from the equation altogether, if no one uses CHAIN4 with anything less than all planes enabled).
|
* (or removing nSeqMapMask from the equation altogether, if CHAIN1 is never used with any planes disabled).
|
||||||
*/
|
*/
|
||||||
var dw = ((b << shift) & this.controller.nSeqMapMask) | (this.adw[idw] & ~((0xff << shift) & this.controller.nSeqMapMask));
|
var dw = ((b << shift) & this.controller.nSeqMapMask) | (this.adw[idw] & ~((0xff << shift) & this.controller.nSeqMapMask));
|
||||||
if (this.adw[idw] != dw) {
|
if (this.adw[idw] != dw) {
|
||||||
|
|
@ -2059,6 +2138,7 @@ Card.ACCESS.afn = [];
|
||||||
|
|
||||||
Card.ACCESS.afn[Card.ACCESS.READ.MODE0] = Card.ACCESS.readByteMode0;
|
Card.ACCESS.afn[Card.ACCESS.READ.MODE0] = Card.ACCESS.readByteMode0;
|
||||||
Card.ACCESS.afn[Card.ACCESS.READ.MODE0 | Card.ACCESS.READ.CHAIN4] = Card.ACCESS.readByteMode0Chain4;
|
Card.ACCESS.afn[Card.ACCESS.READ.MODE0 | Card.ACCESS.READ.CHAIN4] = Card.ACCESS.readByteMode0Chain4;
|
||||||
|
Card.ACCESS.afn[Card.ACCESS.READ.MODE0 | Card.ACCESS.READ.CHAIN1] = Card.ACCESS.readByteMode0Chain1;
|
||||||
Card.ACCESS.afn[Card.ACCESS.READ.MODE0 | Card.ACCESS.READ.EVENODD] = Card.ACCESS.readByteMode0EvenOdd;
|
Card.ACCESS.afn[Card.ACCESS.READ.MODE0 | Card.ACCESS.READ.EVENODD] = Card.ACCESS.readByteMode0EvenOdd;
|
||||||
Card.ACCESS.afn[Card.ACCESS.READ.MODE1] = Card.ACCESS.readByteMode1;
|
Card.ACCESS.afn[Card.ACCESS.READ.MODE1] = Card.ACCESS.readByteMode1;
|
||||||
|
|
||||||
|
|
@ -2068,6 +2148,7 @@ Card.ACCESS.afn[Card.ACCESS.WRITE.MODE0 | Card.ACCESS.WRITE.AND] = Card.ACCESS.
|
||||||
Card.ACCESS.afn[Card.ACCESS.WRITE.MODE0 | Card.ACCESS.WRITE.OR] = Card.ACCESS.writeByteMode0Or;
|
Card.ACCESS.afn[Card.ACCESS.WRITE.MODE0 | Card.ACCESS.WRITE.OR] = Card.ACCESS.writeByteMode0Or;
|
||||||
Card.ACCESS.afn[Card.ACCESS.WRITE.MODE0 | Card.ACCESS.WRITE.XOR] = Card.ACCESS.writeByteMode0Xor;
|
Card.ACCESS.afn[Card.ACCESS.WRITE.MODE0 | Card.ACCESS.WRITE.XOR] = Card.ACCESS.writeByteMode0Xor;
|
||||||
Card.ACCESS.afn[Card.ACCESS.WRITE.MODE0 | Card.ACCESS.WRITE.CHAIN4] = Card.ACCESS.writeByteMode0Chain4;
|
Card.ACCESS.afn[Card.ACCESS.WRITE.MODE0 | Card.ACCESS.WRITE.CHAIN4] = Card.ACCESS.writeByteMode0Chain4;
|
||||||
|
Card.ACCESS.afn[Card.ACCESS.WRITE.MODE0 | Card.ACCESS.WRITE.CHAIN1] = Card.ACCESS.writeByteMode0Chain1;
|
||||||
Card.ACCESS.afn[Card.ACCESS.WRITE.MODE0 | Card.ACCESS.WRITE.EVENODD] = Card.ACCESS.writeByteMode0EvenOdd;
|
Card.ACCESS.afn[Card.ACCESS.WRITE.MODE0 | Card.ACCESS.WRITE.EVENODD] = Card.ACCESS.writeByteMode0EvenOdd;
|
||||||
Card.ACCESS.afn[Card.ACCESS.WRITE.MODE1] = Card.ACCESS.writeByteMode1;
|
Card.ACCESS.afn[Card.ACCESS.WRITE.MODE1] = Card.ACCESS.writeByteMode1;
|
||||||
Card.ACCESS.afn[Card.ACCESS.WRITE.MODE1 | Card.ACCESS.WRITE.EVENODD] = Card.ACCESS.writeByteMode1EvenOdd;
|
Card.ACCESS.afn[Card.ACCESS.WRITE.MODE1 | Card.ACCESS.WRITE.EVENODD] = Card.ACCESS.writeByteMode1EvenOdd;
|
||||||
|
|
@ -3901,7 +3982,7 @@ Video.prototype.checkBlink = function()
|
||||||
* visibility of the cursor (more than these, actually, but I'm going to limit my initial support to standard
|
* visibility of the cursor (more than these, actually, but I'm going to limit my initial support to standard
|
||||||
* ROM BIOS controller settings):
|
* ROM BIOS controller settings):
|
||||||
*
|
*
|
||||||
* CRTC.MAX_SCAN_LINE
|
* CRTC.MAX_SCAN
|
||||||
* CRTC.CURSOR_START
|
* CRTC.CURSOR_START
|
||||||
* CRTC.CURSOR_END
|
* CRTC.CURSOR_END
|
||||||
* CRTC.START_ADDR_HI
|
* CRTC.START_ADDR_HI
|
||||||
|
|
@ -3927,7 +4008,7 @@ Video.prototype.checkCursor = function()
|
||||||
var bCursorFlags = this.cardActive.regCRTData[Card.CRTC.CURSOR_START.INDX];
|
var bCursorFlags = this.cardActive.regCRTData[Card.CRTC.CURSOR_START.INDX];
|
||||||
var bCursorStart = bCursorFlags & Card.CRTC.CURSOR_START.MASK;
|
var bCursorStart = bCursorFlags & Card.CRTC.CURSOR_START.MASK;
|
||||||
var bCursorEnd = this.cardActive.regCRTData[Card.CRTC.CURSOR_END.INDX] & Card.CRTC.CURSOR_END.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_LINE] & Card.CRTC.CURSOR_END.MASK;
|
var bCursorMax = this.cardActive.regCRTData[Card.CRTC.MAX_SCAN.INDX] & Card.CRTC.CURSOR_END.MASK;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* HACK: The original EGA BIOS has a cursor emulation bug when 43-line mode is enabled, so we attempt to detect
|
* HACK: The original EGA BIOS has a cursor emulation bug when 43-line mode is enabled, so we attempt to detect
|
||||||
|
|
@ -4033,7 +4114,7 @@ Video.prototype.getAccess = function()
|
||||||
var nAccess;
|
var nAccess;
|
||||||
var card = this.cardActive;
|
var card = this.cardActive;
|
||||||
|
|
||||||
this.fLinear = false;
|
this.fColor256 = false;
|
||||||
var regGRCMode = card.regGRCData[Card.GRC.MODE.INDX];
|
var regGRCMode = card.regGRCData[Card.GRC.MODE.INDX];
|
||||||
if (regGRCMode != null) {
|
if (regGRCMode != null) {
|
||||||
var nReadAccess = Card.ACCESS.READ.MODE0;
|
var nReadAccess = Card.ACCESS.READ.MODE0;
|
||||||
|
|
@ -4125,10 +4206,15 @@ Video.prototype.getAccess = function()
|
||||||
nReadAccess |= Card.ACCESS.READ.EVENODD;
|
nReadAccess |= Card.ACCESS.READ.EVENODD;
|
||||||
nWriteAccess |= Card.ACCESS.WRITE.EVENODD;
|
nWriteAccess |= Card.ACCESS.WRITE.EVENODD;
|
||||||
}
|
}
|
||||||
if (regSEQMode & Card.SEQ.MEMMODE.CHAIN4) {
|
if (regGRCMode & Card.GRC.MODE.COLOR256) {
|
||||||
nReadAccess |= Card.ACCESS.READ.CHAIN4;
|
if (regSEQMode & Card.SEQ.MEMMODE.CHAIN4) {
|
||||||
nWriteAccess |= Card.ACCESS.WRITE.CHAIN4;
|
nReadAccess |= Card.ACCESS.READ.CHAIN4;
|
||||||
this.fLinear = true;
|
nWriteAccess |= Card.ACCESS.WRITE.CHAIN4;
|
||||||
|
} else {
|
||||||
|
nReadAccess |= Card.ACCESS.READ.CHAIN1;
|
||||||
|
nWriteAccess |= Card.ACCESS.WRITE.CHAIN1;
|
||||||
|
}
|
||||||
|
this.fColor256 = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nAccess = nReadAccess | nWriteAccess;
|
nAccess = nReadAccess | nWriteAccess;
|
||||||
|
|
@ -4197,7 +4283,7 @@ Video.prototype.setDimensions = function()
|
||||||
* then we'll need to load another MDA font variation, because we only load an 9x14 font for MDA.
|
* 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.cardActive === this.cardEGA && this.nFont == Video.FONT.CGA) {
|
||||||
if (this.cardEGA.regCRTData[Card.CRTC.MAX_SCAN_LINE] == 7) {
|
if (this.cardEGA.regCRTData[Card.CRTC.MAX_SCAN.INDX] == 7) {
|
||||||
/*
|
/*
|
||||||
* Vertical resolution of 350 divided by 8 (ie, scan lines 0-7) yields 43 whole rows.
|
* Vertical resolution of 350 divided by 8 (ie, scan lines 0-7) yields 43 whole rows.
|
||||||
*/
|
*/
|
||||||
|
|
@ -4207,7 +4293,7 @@ Video.prototype.setDimensions = function()
|
||||||
* Since we can also be called before any hardware registers have been initialized,
|
* 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).
|
* 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_LINE] == 13) */ {
|
else /* if (this.cardEGA.regCRTData[Card.CRTC.MAX_SCAN.INDX] == 13) */ {
|
||||||
/*
|
/*
|
||||||
* Vertical resolution of 350 divided by 14 (ie, scan lines 0-13) yields exactly 25 rows.
|
* Vertical resolution of 350 divided by 14 (ie, scan lines 0-13) yields exactly 25 rows.
|
||||||
*
|
*
|
||||||
|
|
@ -4415,10 +4501,10 @@ Video.prototype.checkMode = function(fForce)
|
||||||
}
|
}
|
||||||
|
|
||||||
var fSEQDotClock = (card.regSEQData[Card.SEQ.CLOCKING.INDX] & Card.SEQ.CLOCKING.DOTCLOCK);
|
var fSEQDotClock = (card.regSEQData[Card.SEQ.CLOCKING.INDX] & Card.SEQ.CLOCKING.DOTCLOCK);
|
||||||
var nCRTCVertTotal = card.regCRTData[Card.CRTC.EGA.VERT_TOTAL];
|
var nCRTCVertTotal = card.regCRTData[Card.CRTC.EGA.VTOTAL];
|
||||||
nCRTCVertTotal |= ((card.regCRTData[Card.CRTC.EGA.OVERFLOW.INDX] & Card.CRTC.EGA.OVERFLOW.VERT_TOTAL_BIT8)? 0x100 : 0);
|
nCRTCVertTotal |= ((card.regCRTData[Card.CRTC.EGA.OVERFLOW.INDX] & Card.CRTC.EGA.OVERFLOW.VTOTAL_BIT8)? 0x100 : 0);
|
||||||
if (card.nCard == Video.CARD.VGA) {
|
if (card.nCard == Video.CARD.VGA) {
|
||||||
nCRTCVertTotal |= ((card.regCRTData[Card.CRTC.EGA.OVERFLOW.INDX] & Card.CRTC.EGA.OVERFLOW.VERT_TOTAL_BIT9)? 0x200 : 0);
|
nCRTCVertTotal |= ((card.regCRTData[Card.CRTC.EGA.OVERFLOW.INDX] & Card.CRTC.EGA.OVERFLOW.VTOTAL_BIT9)? 0x200 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nMode != Video.MODE.UNKNOWN) {
|
if (nMode != Video.MODE.UNKNOWN) {
|
||||||
|
|
@ -4437,8 +4523,11 @@ Video.prototype.checkMode = function(fForce)
|
||||||
* we've already defaulted to 0x0F or 0x10, so determine if it's 0x0D or 0x0E (ie, a 200-row mode)
|
* we've already defaulted to 0x0F or 0x10, so determine if it's 0x0D or 0x0E (ie, a 200-row mode)
|
||||||
* and then which one (ie, 320 wide or 640 wide).
|
* and then which one (ie, 320 wide or 640 wide).
|
||||||
*/
|
*/
|
||||||
if (card.regSEQData[Card.SEQ.MEMMODE.INDX] & Card.SEQ.MEMMODE.CHAIN4) {
|
if (card.regGRCData[Card.GRC.MODE.INDX] & Card.GRC.MODE.COLOR256) {
|
||||||
nMode = Video.MODE.VGA_320X200;
|
nMode = Video.MODE.VGA_320X200;
|
||||||
|
if (!(card.regCRTData[Card.CRTC.MAX_SCAN.INDX] & Card.CRTC.MAX_SCAN.SCAN_LINE)) {
|
||||||
|
nMode = Video.MODE.VGA_320X400;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (nCRTCVertTotal < 500) {
|
else if (nCRTCVertTotal < 500) {
|
||||||
if (nCRTCVertTotal < 350) {
|
if (nCRTCVertTotal < 350) {
|
||||||
|
|
@ -4839,17 +4928,17 @@ Video.prototype.updateScreen = function(fForce)
|
||||||
var addrScreenLimit = addrScreen + card.sizeBuffer;
|
var addrScreenLimit = addrScreen + card.sizeBuffer;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* HACK: nStartAddress is supposed to be "latched" ONLY at the start of every VERT_RETRACE interval;
|
* HACK: nStartAddress is supposed to be "latched" ONLY at the start of every VRETRACE interval;
|
||||||
* this is an attempt to honor that behavior, but unfortunately, updateScreen() is currently called at
|
* 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
|
* 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()).
|
* 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
|
* 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
|
* that CPU bursts are in sync with VRETRACE. 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
|
* factors, such as the horizontal retrace interval, and the timing requirements of other cards in a
|
||||||
* multi-display configuration.
|
* multi-display configuration.
|
||||||
*/
|
*/
|
||||||
if (this.getRetraceBits(card) & Card.CGA.STATUS.VERT_RETRACE) {
|
if (this.getRetraceBits(card) & Card.CGA.STATUS.VRETRACE) {
|
||||||
card.nStartAddress = ((card.regCRTData[Card.CRTC.START_ADDR_HI] << 8) + card.regCRTData[Card.CRTC.START_ADDR_LO])|0;
|
card.nStartAddress = ((card.regCRTData[Card.CRTC.START_ADDR_HI] << 8) + card.regCRTData[Card.CRTC.START_ADDR_LO])|0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4865,15 +4954,18 @@ Video.prototype.updateScreen = function(fForce)
|
||||||
addrScreen += offScreen;
|
addrScreen += offScreen;
|
||||||
var cbScreen = this.cbScreen;
|
var cbScreen = this.cbScreen;
|
||||||
|
|
||||||
if (this.nCard >= Video.CARD.EGA && card.regCRTData[Card.CRTC.EGA.OFFSET]) {
|
if (this.nCard >= Video.CARD.EGA && card.regCRTData[Card.CRTC.EGA.OFFSET] && (card.regCRTData[Card.CRTC.EGA.OFFSET] << 1) != card.regCRTData[Card.CRTC.EGA.HDISP_END] + 1) {
|
||||||
/*
|
/*
|
||||||
* Pre-EGA, the extent of visible screen memory (cbScreen) was derived from nCols * nRows, but since
|
* 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).
|
* 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
|
* 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).
|
* cbScreen was computed (but without any CGA-related padding considerations).
|
||||||
|
*
|
||||||
|
* TODO: I'm taking a lot of shortcuts in this calculation (eg, relying on nFont to detect text modes,
|
||||||
|
* ignoring MODE_CTRL.BM, etc); generalize this someday.
|
||||||
*/
|
*/
|
||||||
this.nColsLogical = card.regCRTData[Card.CRTC.EGA.OFFSET] << (this.nFont? 1 : (card.regCRTData[Card.CRTC.EGA.UNDERLINE.INDX] & Card.CRTC.EGA.UNDERLINE.DWORD)? 3 : 4);
|
this.nColsLogical = card.regCRTData[Card.CRTC.EGA.OFFSET] << (this.nFont? 1 : (card.regCRTData[Card.CRTC.EGA.UNDERLINE.INDX] & Card.CRTC.EGA.UNDERLINE.DWORD)? 3 : 4);
|
||||||
cbScreen = ((((this.nColsLogical * (this.nRows-1) + this.nCols) / this.nCellsPerWord) << 1) + this.cbPadding)|0;
|
cbScreen = (((this.nColsLogical * (this.nRows-1) + this.nCols) / this.nCellsPerWord) << 1)|0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addrScreen + cbScreen > addrScreenLimit) {
|
if (addrScreen + cbScreen > addrScreenLimit) {
|
||||||
|
|
@ -4925,7 +5017,7 @@ Video.prototype.updateScreen = function(fForce)
|
||||||
else if (this.cbSplit) {
|
else if (this.cbSplit) {
|
||||||
this.updateScreenGraphicsCGA(addrScreen, addrScreenLimit);
|
this.updateScreenGraphicsCGA(addrScreen, addrScreenLimit);
|
||||||
}
|
}
|
||||||
else if (!this.fLinear) {
|
else if (!this.fColor256) {
|
||||||
this.updateScreenGraphicsEGA(addrScreen, addrScreenLimit);
|
this.updateScreenGraphicsEGA(addrScreen, addrScreenLimit);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -5199,10 +5291,11 @@ Video.prototype.updateScreenGraphicsEGA = function(addrScreen, addrScreenLimit)
|
||||||
/**
|
/**
|
||||||
* updateScreenGraphicsVGA(addrScreen, addrScreenLimit)
|
* updateScreenGraphicsVGA(addrScreen, addrScreenLimit)
|
||||||
*
|
*
|
||||||
* The name is a slight misnomer: updateScreenGraphicsEGA() takes care of all the "planar" video modes, which were
|
* This function name is a slight misnomer: updateScreenGraphicsEGA() takes care of all the "planar" video modes
|
||||||
* first introduced by the EGA and later expanded by the VGA, whereas this function takes care of just the "linear"
|
* (first introduced by the EGA and later expanded by the VGA), where each pixel's bits are spread across multiple
|
||||||
* video modes introduced by the VGA, such as mode 0x13 (320x200x256). Those modes may also be referred to as CHAIN4
|
* planes, whereas this function takes care of just the "linear" video modes introduced by the VGA, such as mode 0x13
|
||||||
* modes, since I think all of them require that the CHAIN4 bit in the Sequencer's MEMMODE register be set.
|
* (320x200x256), where each pixel's bits are contained within a single plane. This is basically all 256-color 8bpp
|
||||||
|
* (CHAIN4, CHAIN1, etc) modes, hence the hard-coded call to getCardColors(8).
|
||||||
*
|
*
|
||||||
* @param addrScreen
|
* @param addrScreen
|
||||||
* @param addrScreenLimit
|
* @param addrScreenLimit
|
||||||
|
|
@ -5306,7 +5399,7 @@ Video.prototype.updateScreenGraphicsVGA = function(addrScreen, addrScreenLimit)
|
||||||
/**
|
/**
|
||||||
* getRetraceBits(card)
|
* getRetraceBits(card)
|
||||||
*
|
*
|
||||||
* This returns a byte value with two bits set or clear as appropriate: DISP_RETRACE and VERT_RETRACE.
|
* This returns a byte value with two bits set or clear as appropriate: RETRACE and VRETRACE.
|
||||||
*
|
*
|
||||||
* @this {Video}
|
* @this {Video}
|
||||||
* @param {Object} card
|
* @param {Object} card
|
||||||
|
|
@ -5317,7 +5410,7 @@ Video.prototype.getRetraceBits = function(card)
|
||||||
var b = 0;
|
var b = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NOTE: The CGA bits CGA.STATUS.DISP_RETRACE (0x01) and CGA.STATUS.VERT_RETRACE (0x08) match the EGA definitions,
|
* NOTE: The CGA bits CGA.STATUS.RETRACE (0x01) and CGA.STATUS.VRETRACE (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
|
* 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.
|
* the MDA uses different designations, but the bits appear to serve the same purpose.
|
||||||
*
|
*
|
||||||
|
|
@ -5332,9 +5425,9 @@ Video.prototype.getRetraceBits = function(card)
|
||||||
nElapsedCycles = -nElapsedCycles|0;
|
nElapsedCycles = -nElapsedCycles|0;
|
||||||
}
|
}
|
||||||
var nCyclesHorzRemain = nElapsedCycles % card.nCyclesHorzPeriod;
|
var nCyclesHorzRemain = nElapsedCycles % card.nCyclesHorzPeriod;
|
||||||
if (nCyclesHorzRemain > card.nCyclesHorzActive) b |= Card.CGA.STATUS.DISP_RETRACE;
|
if (nCyclesHorzRemain > card.nCyclesHorzActive) b |= Card.CGA.STATUS.RETRACE;
|
||||||
var nCyclesVertRemain = nElapsedCycles % card.nCyclesVertPeriod;
|
var nCyclesVertRemain = nElapsedCycles % card.nCyclesVertPeriod;
|
||||||
if (nCyclesVertRemain > card.nCyclesVertActive) b |= Card.CGA.STATUS.VERT_RETRACE | Card.CGA.STATUS.DISP_RETRACE;
|
if (nCyclesVertRemain > card.nCyclesVertActive) b |= Card.CGA.STATUS.VRETRACE | Card.CGA.STATUS.RETRACE;
|
||||||
/*
|
/*
|
||||||
* This is optional: the number of CPU cycles that remain in the current vertical period is all we need to keep
|
* 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
|
* track of (the number of cycles since the card was initialized is fine, too, but that delta can become extremely
|
||||||
|
|
@ -5503,7 +5596,7 @@ Video.prototype.outATC = function(port, bOut, addrFrom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* HACK: nStartAddress is supposed to be "latched" ONLY at the start of every VERT_RETRACE interval,
|
* HACK: nStartAddress is supposed to be "latched" ONLY at the start of every VRETRACE interval,
|
||||||
* but other "triggers" are currently required; see updateScreen() for details.
|
* 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;
|
this.cardEGA.nStartAddress = ((this.cardEGA.regCRTData[Card.CRTC.START_ADDR_HI] << 8) + this.cardEGA.regCRTData[Card.CRTC.START_ADDR_LO])|0;
|
||||||
|
|
@ -6222,23 +6315,23 @@ Video.prototype.outCRTCData = function(card, port, bOut, addrFrom)
|
||||||
}
|
}
|
||||||
if (card.regCRTIndx == Card.CRTC.START_ADDR_HI || card.regCRTIndx == Card.CRTC.START_ADDR_LO) {
|
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,
|
* HACK: nStartAddress is supposed to be "latched" ONLY at the start of every VRETRACE interval,
|
||||||
* but the best we can currently do is latch it during retrace, as well as other times (eg, see outATC()).
|
* 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) {
|
if (this.getRetraceBits(card) & Card.CGA.STATUS.RETRACE) {
|
||||||
card.nStartAddress = ((card.regCRTData[Card.CRTC.START_ADDR_HI] << 8) + card.regCRTData[Card.CRTC.START_ADDR_LO])|0;
|
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,
|
* 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
|
* and if that's all that's happening with Card.CRTC.MAX_SCAN.INDX, then we don't want to treat
|
||||||
* it special; let the mode change be detected normally (eg, when the GRC regs are written later).
|
* it special; let the mode change be detected normally (eg, when the GRC regs are written later).
|
||||||
*
|
*
|
||||||
* On the other hand, if this was an out-of-sequence write to Card.CRTC.MAX_SCAN_LINE, then
|
* On the other hand, if this was an out-of-sequence write to Card.CRTC.MAX_SCAN.INDX, then
|
||||||
* yes, we want to force setMode() to call setDimensions(), which is key to setting the proper
|
* yes, we want to force setMode() to call setDimensions(), which is key to setting the proper
|
||||||
* number of screen rows.
|
* number of screen rows.
|
||||||
*/
|
*/
|
||||||
if (card.regCRTIndx == Card.CRTC.MAX_SCAN_LINE && card.regCRTPrev != Card.CRTC.MAX_SCAN_LINE-1) {
|
if (card.regCRTIndx == Card.CRTC.MAX_SCAN.INDX && card.regCRTPrev != Card.CRTC.MAX_SCAN.INDX-1) {
|
||||||
this.checkMode(true);
|
this.checkMode(true);
|
||||||
}
|
}
|
||||||
this.checkCursor();
|
this.checkCursor();
|
||||||
|
|
@ -6339,7 +6432,7 @@ Video.prototype.inCardStatus = function(card, addrFrom)
|
||||||
* TODO: Decide whether to preserve the bits from getRetraceBits() on the MDA/CGA; we're continuing
|
* 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.
|
* 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;
|
b = (card.regStatus ^= (Card.CGA.STATUS.RETRACE | Card.CGA.STATUS.VRETRACE)) | 0xf0;
|
||||||
}
|
}
|
||||||
|
|
||||||
card.regStatus = b;
|
card.regStatus = b;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue