Scaffolding for VGA CHAIN4 support
This commit is contained in:
parent
965ee01f99
commit
718073f45f
9 changed files with 869 additions and 30 deletions
|
|
@ -7,7 +7,7 @@ root = true
|
|||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
[*.bas]
|
||||
[*.BAS,*.bas]
|
||||
indent_style = space
|
||||
indent_size = 8
|
||||
trim_trailing_whitespace = true
|
||||
|
|
@ -17,6 +17,11 @@ indent_style = tab
|
|||
indent_size = 8
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[{*.C,*.H,*.c,*.h}]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[modules/**.js]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
|
|
|||
|
|
@ -636,8 +636,8 @@ Memory.prototype = {
|
|||
*/
|
||||
writeShortDefault: function writeShortDefault(off, w, addr) {
|
||||
Component.assert(!(w & ~0xffff));
|
||||
this.writeByte(off, w & 0xff);
|
||||
this.writeByte(off + 1, w >> 8);
|
||||
this.writeByte(off, w & 0xff, addr);
|
||||
this.writeByte(off + 1, w >> 8, addr);
|
||||
},
|
||||
/**
|
||||
* writeLongDefault(off, w, addr)
|
||||
|
|
@ -648,10 +648,10 @@ Memory.prototype = {
|
|||
* @param {number} addr
|
||||
*/
|
||||
writeLongDefault: function writeLongDefault(off, w, addr) {
|
||||
this.writeByte(off, w & 0xff);
|
||||
this.writeByte(off + 1, (w >> 8) & 0xff);
|
||||
this.writeByte(off + 2, (w >> 16) & 0xff);
|
||||
this.writeByte(off + 3, (w >>> 24));
|
||||
this.writeByte(off, w & 0xff, addr);
|
||||
this.writeByte(off + 1, (w >> 8) & 0xff, addr);
|
||||
this.writeByte(off + 2, (w >> 16) & 0xff, addr);
|
||||
this.writeByte(off + 3, (w >>> 24), addr);
|
||||
},
|
||||
/**
|
||||
* readByteMemory(off, addr)
|
||||
|
|
|
|||
|
|
@ -703,7 +703,7 @@ Video.FONT = {
|
|||
*
|
||||
* 0: # of columns (nCols)
|
||||
* 1: # of rows (nRows)
|
||||
* 2: # cells per word (nCellsPerWord: # of characters or pixels per word)
|
||||
* 2: # cells per word (nCellsPerWord: # of characters or pixels per 16-bit word)
|
||||
* 3: # bytes of visible screen padding, if any (used for CGA graphics modes only)
|
||||
* 4: font ID (nFont: undefined if graphics mode)
|
||||
*
|
||||
|
|
@ -729,7 +729,7 @@ Video.aModeParms[Video.MODE.EGA_640X350_MONO] = [640, 350, 16];
|
|||
Video.aModeParms[Video.MODE.EGA_640X350] = [640, 350, 16]; // 0x10
|
||||
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_320X200] = [320, 200, 16]; // 0x13
|
||||
Video.aModeParms[Video.MODE.VGA_320X200] = [320, 200, 2]; // 0x13
|
||||
|
||||
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
|
||||
|
|
@ -1243,7 +1243,7 @@ Card.ATC = {
|
|||
TEXTGRCC: 0x04, // bit 2: set for line graphics in character codes 0xC0-0xDF, clear otherwise
|
||||
TEXTBLINK: 0x08, // bit 3: set for text blink attribute, clear for background intensity attribute
|
||||
RESERVED: 0x10, // bit 4: reserved
|
||||
PANCOMPAT: 0x20, // bit 5: set for PEL panning compatibility
|
||||
PANCOMPAT: 0x20, // bit 5: set for pixel-panning compatibility
|
||||
PELWIDTH: 0x40, // bit 6: set for 256-color modes, clear for all other modes
|
||||
COLORSEL: 0x80 // bit 7: set for P5,P4 mapped to bits 1,0 of the Color Select register
|
||||
},
|
||||
|
|
@ -1258,7 +1258,7 @@ Card.ATC = {
|
|||
},
|
||||
HORZPAN: {
|
||||
INDX: 0x13, // ATC Horizontal PEL Panning Register
|
||||
SHIFT_LEFT: 0x0F // bits 0-3 indicate # of PELs to shift left
|
||||
SHIFT_LEFT: 0x0F // bits 0-3 indicate # of pixels to shift left
|
||||
},
|
||||
COLORSEL: {
|
||||
INDX: 0x14, // ATC Color Select Register (VGA only)
|
||||
|
|
@ -1383,11 +1383,11 @@ if (DEBUGGER) Card.SEQ.REGS = ["RESET","CLOCKING","MAPMASK","CHARMAP","MEMMODE"]
|
|||
/*
|
||||
* VGA Digital-to-Analog Converter (DAC) Registers (regDACMask, regDACState, regDACAddr, and regDACData)
|
||||
*
|
||||
* To write PEL data, write an address to DAC.ADDR.PORT_WRITE, then write 3 bytes to DAC.DATA.PORT; the low 6 bits
|
||||
* To write DAC data, write an address to DAC.ADDR.PORT_WRITE, then write 3 bytes to DAC.DATA.PORT; the low 6 bits
|
||||
* of each byte will be concatenated to form an 18-bit DAC value (red is least significant, followed by green, then blue).
|
||||
* When the final byte is received, the 18-bit DAC value is updated and regDACAddr is auto-incremented.
|
||||
*
|
||||
* To read PEL data, the process is similar, but the initial address is written to DAC.ADDR.PORT_READ instead.
|
||||
* To read DAC data, the process is similar, but the initial address is written to DAC.ADDR.PORT_READ instead.
|
||||
*
|
||||
* DAC.STATE.PORT and DAC.ADDR.PORT_WRITE can be read at any time and will not interfere with a read or write operation
|
||||
* in progress. To prevent "snow", reading or writing DAC values should be limited to retrace intervals (see regStatus1),
|
||||
|
|
@ -1568,6 +1568,7 @@ Card.ACCESS = {
|
|||
MODE0: 0x0400,
|
||||
MODE1: 0x0500,
|
||||
EVENODD: 0x1000,
|
||||
CHAIN4: 0x4000,
|
||||
MASK: 0xFF00
|
||||
},
|
||||
WRITE: { // and WRITE values are designed to be OR'ed with READ values
|
||||
|
|
@ -1575,6 +1576,7 @@ Card.ACCESS = {
|
|||
MODE1: 0x0001,
|
||||
MODE2: 0x0002,
|
||||
MODE3: 0x0003, // VGA only
|
||||
CHAIN4: 0x0004,
|
||||
EVENODD: 0x0010,
|
||||
ROT: 0x0020,
|
||||
AND: 0x0060,
|
||||
|
|
@ -1620,6 +1622,23 @@ Card.ACCESS.readByteMode0 = function readByteMode0(off, addr)
|
|||
return (dw >> this.controller.nReadMapShift) & 0xff;
|
||||
};
|
||||
|
||||
/**
|
||||
* readByteMode0Chain4(off, addr)
|
||||
*
|
||||
* See writeByteMode0Chain4 for a description of how writes are distributed across planes.
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} off
|
||||
* @param {number} [addr]
|
||||
* @return {number}
|
||||
*/
|
||||
Card.ACCESS.readByteMode0Chain4 = function readByteMode0Chain4(off, addr)
|
||||
{
|
||||
var idw = (off & ~0x3) + this.offset;
|
||||
var shift = (off & 0x3) << 3;
|
||||
return ((this.controller.latches = this.adw[idw]) >> shift) & 0xff;
|
||||
};
|
||||
|
||||
/**
|
||||
* readByteMode0EvenOdd(off, addr)
|
||||
*
|
||||
|
|
@ -1713,6 +1732,44 @@ Card.ACCESS.writeByteMode0 = function writeByteMode0(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
|
||||
* all planes are enabled by the Sequencer's MAPMASK register (which we assume still controls access):
|
||||
*
|
||||
* off idw adw[idw]
|
||||
* ------ ------ ----------
|
||||
* 0x0000: 0x0000 0x000000ff
|
||||
* 0x0001: 0x0000 0x0000ff00
|
||||
* 0x0002: 0x0000 0x00ff0000
|
||||
* 0x0003: 0x0000 0xff000000
|
||||
* 0x0004: 0x0001 0x000000ff
|
||||
* 0x0005: 0x0001 0x0000ff00
|
||||
* 0x0006: 0x0001 0x00ff0000
|
||||
* 0x0007: 0x0001 0xff000000
|
||||
* ...
|
||||
*
|
||||
* @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 whenever nSeqMapMask is -1
|
||||
* (or removing nSeqMapMask from the equation altogether, if no one uses CHAIN4 with anything less than all planes enabled).
|
||||
*/
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* writeByteMode0EvenOdd(off, b, addr)
|
||||
*
|
||||
|
|
@ -1726,8 +1783,8 @@ Card.ACCESS.writeByteMode0EvenOdd = function writeByteMode0EvenOdd(off, b, addr)
|
|||
off += this.offset;
|
||||
var dw = b | (b << 8) | (b << 16) | (b << 24);
|
||||
//
|
||||
// When even/odd addressing is enabled, nSeqMapMask must be cleared for planes 1 and 3
|
||||
// if the address is even, and cleared for planes 0 and 2 if the address is odd.
|
||||
// When even/odd addressing is enabled, nSeqMapMask must be cleared for planes 1
|
||||
// and 3 if the address is even, and cleared for planes 0 and 2 if the address is odd.
|
||||
//
|
||||
var idw = off & ~0x1;
|
||||
dw = (dw & this.controller.nBitMapMask) | (this.controller.latches & ~this.controller.nBitMapMask);
|
||||
|
|
@ -1859,8 +1916,8 @@ Card.ACCESS.writeByteMode1 = function writeByteMode1(off, b, addr)
|
|||
Card.ACCESS.writeByteMode1EvenOdd = function writeByteMode1EvenOdd(off, b, addr)
|
||||
{
|
||||
/*
|
||||
* TODO: As discussed in getAccess(), we need to run some tests on real EGA/VGA hardware to determine
|
||||
* exactly where latches are written (ie, to which address) when EVENODD is in effect.
|
||||
* TODO: As discussed in getAccess(), we need to run some tests on real EGA/VGA hardware to
|
||||
* determine exactly where latches are written (ie, to which address) when EVENODD is in effect.
|
||||
*/
|
||||
off += this.offset;
|
||||
//
|
||||
|
|
@ -1993,7 +2050,8 @@ Card.ACCESS.writeByteMode3 = function writeByteMode3(off, b, addr)
|
|||
Card.ACCESS.afn = [];
|
||||
|
||||
Card.ACCESS.afn[Card.ACCESS.READ.MODE0] = Card.ACCESS.readByteMode0;
|
||||
Card.ACCESS.afn[Card.ACCESS.READ.MODE0 | Card.ACCESS.READ.EVENODD] = Card.ACCESS.readByteMode0EvenOdd;
|
||||
Card.ACCESS.afn[Card.ACCESS.READ.MODE0 | Card.ACCESS.READ.CHAIN4] = Card.ACCESS.readByteMode0Chain4;
|
||||
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.WRITE.MODE0] = Card.ACCESS.writeByteMode0;
|
||||
|
|
@ -2001,6 +2059,7 @@ Card.ACCESS.afn[Card.ACCESS.WRITE.MODE0 | Card.ACCESS.WRITE.ROT] = Card.ACCESS.
|
|||
Card.ACCESS.afn[Card.ACCESS.WRITE.MODE0 | Card.ACCESS.WRITE.AND] = Card.ACCESS.writeByteMode0And;
|
||||
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.CHAIN4] = Card.ACCESS.writeByteMode0Chain4;
|
||||
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.WRITE.EVENODD] = Card.ACCESS.writeByteMode1EvenOdd;
|
||||
|
|
@ -3929,6 +3988,7 @@ Video.prototype.getAccess = function()
|
|||
var nAccess;
|
||||
var card = this.cardActive;
|
||||
|
||||
this.fLinear = false;
|
||||
var regGRCMode = card.regGRCData[Card.GRC.MODE.INDX];
|
||||
if (regGRCMode != null) {
|
||||
var nReadAccess = Card.ACCESS.READ.MODE0;
|
||||
|
|
@ -4020,6 +4080,11 @@ Video.prototype.getAccess = function()
|
|||
nReadAccess |= Card.ACCESS.READ.EVENODD;
|
||||
nWriteAccess |= Card.ACCESS.WRITE.EVENODD;
|
||||
}
|
||||
if (regSEQMode & Card.SEQ.MEMMODE.CHAIN4) {
|
||||
nReadAccess |= Card.ACCESS.READ.CHAIN4;
|
||||
nWriteAccess |= Card.ACCESS.WRITE.CHAIN4;
|
||||
this.fLinear = true;
|
||||
}
|
||||
}
|
||||
nAccess = nReadAccess | nWriteAccess;
|
||||
}
|
||||
|
|
@ -4114,6 +4179,7 @@ Video.prototype.setDimensions = function()
|
|||
this.nCellCache = (this.nCells / this.nCellsPerWord)|0;
|
||||
this.cbScreen = ((this.nCellCache << 1) + this.cbPadding)|0;
|
||||
this.cbSplit = (this.cbPadding? ((this.cbScreen + this.cbPadding) >> 1) : 0);
|
||||
this.fLinear = false; // set for 8bpp "linear" VGA modes only
|
||||
if (this.nMode >= Video.MODE.EGA_320X200) this.nCellCache <<= 1; // double nCellCache (every cell is a byte)
|
||||
|
||||
/*
|
||||
|
|
@ -4316,18 +4382,21 @@ Video.prototype.checkMode = function(fForce)
|
|||
if (fSEQDotClock) nMode -= 2;
|
||||
} else {
|
||||
if (card.addrBuffer == 0xB8000) {
|
||||
//
|
||||
// Since nMode will have been assigned a default of either 0x02 or 0x03, convert that to either
|
||||
// 0x05 or 0x04 if we're in a low-res graphics mode, 0x06 otherwise.
|
||||
//
|
||||
/*
|
||||
* Since nMode will have been assigned a default of either 0x02 or 0x03, convert that to either
|
||||
* 0x05 or 0x04 if we're in a low-res graphics mode, 0x06 otherwise.
|
||||
*/
|
||||
nMode = fSEQDotClock? (7 - nMode) : Video.MODE.CGA_640X200;
|
||||
} else {
|
||||
//
|
||||
// card.addrBuffer must be 0xA0000, so we need to discriminate between modes 0x0D through 0x10;
|
||||
// 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).
|
||||
//
|
||||
if (nCRTCVertTotal < 500) {
|
||||
/*
|
||||
* card.addrBuffer must be 0xA0000, so we need to discriminate between modes 0x0D through 0x10;
|
||||
* 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).
|
||||
*/
|
||||
if (card.regSEQData[Card.SEQ.MEMMODE.INDX] & Card.SEQ.MEMMODE.CHAIN4) {
|
||||
nMode = Video.MODE.VGA_320X200;
|
||||
}
|
||||
else if (nCRTCVertTotal < 500) {
|
||||
if (nCRTCVertTotal < 350) {
|
||||
nMode = (fSEQDotClock? Video.MODE.EGA_320X200 : Video.MODE.EGA_640X200);
|
||||
}
|
||||
|
|
@ -4811,9 +4880,12 @@ Video.prototype.updateScreen = function(fForce)
|
|||
else if (this.cbSplit) {
|
||||
this.updateScreenGraphicsCGA(addrScreen, addrScreenLimit);
|
||||
}
|
||||
else {
|
||||
else if (!this.fLinear) {
|
||||
this.updateScreenGraphicsEGA(addrScreen, addrScreenLimit);
|
||||
}
|
||||
else {
|
||||
this.updateScreenGraphicsVGA(addrScreen, addrScreenLimit);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -4994,7 +5066,7 @@ Video.prototype.updateScreenGraphicsEGA = function(addrScreen, addrScreenLimit)
|
|||
data = adwMemory[idw];
|
||||
|
||||
/*
|
||||
* Figure out how many visible pixels this byte represents; usually 8, unless panning is being used.
|
||||
* Figure out how many visible pixels this data represents; usually 8, unless panning is being used.
|
||||
*/
|
||||
var iPixel, nPixels = 8;
|
||||
|
||||
|
|
@ -5079,6 +5151,110 @@ Video.prototype.updateScreenGraphicsEGA = function(addrScreen, addrScreenLimit)
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* updateScreenGraphicsVGA(addrScreen, addrScreenLimit)
|
||||
*
|
||||
* The name is a slight misnomer: updateScreenGraphicsEGA() takes care of all the "planar" video modes, which were
|
||||
* first introduced by the EGA and later expanded by the VGA, whereas this function takes care of just the "linear"
|
||||
* video modes introduced by the VGA, such as mode 0x13 (320x200x256). Those modes may also be referred to as CHAIN4
|
||||
* modes, since I think all of them require that the CHAIN4 bit in the Sequencer's MEMMODE register be set.
|
||||
*
|
||||
* @param addrScreen
|
||||
* @param addrScreenLimit
|
||||
*/
|
||||
Video.prototype.updateScreenGraphicsVGA = function(addrScreen, addrScreenLimit)
|
||||
{
|
||||
var addr, data;
|
||||
|
||||
addr = addrScreen;
|
||||
this.cBlinkVisible = 0;
|
||||
|
||||
var iCell = 0;
|
||||
var aPixelColors = this.getCardColors();
|
||||
var adwMemory = this.cardActive.adwMemory;
|
||||
|
||||
var x = 0, y = 0;
|
||||
var xDirty = this.nCols, xMaxDirty = 0, yDirty = this.nRows, yMaxDirty = 0;
|
||||
|
||||
var iPixelFirst = this.cardActive.regATCData[Card.ATC.HORZPAN.INDX] & Card.ATC.HORZPAN.SHIFT_LEFT;
|
||||
/*
|
||||
* TODO: What should happen if the card is programmed such that nColsLogical is LESS THAN nCols?
|
||||
*/
|
||||
var nRowAdjust = (this.nColsLogical > this.nCols? ((this.nColsLogical - this.nCols - iPixelFirst) >> 3) : 0);
|
||||
|
||||
while (addr < addrScreenLimit) {
|
||||
var idw = addr++ - this.addrBuffer;
|
||||
this.assert(idw >= 0 && idw < adwMemory.length);
|
||||
data = adwMemory[idw];
|
||||
|
||||
/*
|
||||
* Figure out how many visible pixels this data represents; usually 4, unless panning is being used.
|
||||
*/
|
||||
var iPixel, nPixels = 4;
|
||||
|
||||
if (iPixelFirst) {
|
||||
/*
|
||||
* Notice that we're not using the cell cache when panning is active, because the cached cell data no
|
||||
* longer aligns with the data we're pulling out of the video buffer, and it's not clear that the effort
|
||||
* to realign the data and make a valid cache comparison would save enough work to make it worthwhile.
|
||||
*/
|
||||
if (!x) {
|
||||
data <<= iPixelFirst;
|
||||
nPixels -= iPixelFirst;
|
||||
/*
|
||||
* This is as good a place as any to invalidate the cell cache when panning is active; this ensures
|
||||
* we don't rely on stale cache contents once panning stops.
|
||||
*/
|
||||
this.fCellCacheValid = false;
|
||||
} else {
|
||||
iPixel = this.nCols - x;
|
||||
if (nPixels > iPixel) nPixels = iPixel;
|
||||
}
|
||||
} else {
|
||||
this.assert(iCell < this.aCellCache.length);
|
||||
if (this.fCellCacheValid && data === this.aCellCache[iCell]) {
|
||||
x += nPixels;
|
||||
nPixels = 0;
|
||||
} else {
|
||||
this.aCellCache[iCell] = data;
|
||||
}
|
||||
iCell++;
|
||||
}
|
||||
|
||||
if (nPixels) {
|
||||
if (x < xDirty) xDirty = x;
|
||||
for (iPixel = 0; iPixel < nPixels; iPixel++) {
|
||||
var bPixel = data & 0xff;
|
||||
this.setPixel(this.imageScreenBuffer, x++, y, aPixelColors[bPixel]);
|
||||
data >>>= 8;
|
||||
}
|
||||
if (x > xMaxDirty) xMaxDirty = x;
|
||||
if (y < yDirty) yDirty = y;
|
||||
if (y >= yMaxDirty) yMaxDirty = y + 1;
|
||||
}
|
||||
|
||||
this.assert(x <= this.nCols);
|
||||
|
||||
if (x >= this.nCols) {
|
||||
x = 0;
|
||||
if (++y > this.nRows) break;
|
||||
addr += nRowAdjust;
|
||||
}
|
||||
}
|
||||
|
||||
if (!iPixelFirst) this.fCellCacheValid = true;
|
||||
|
||||
/*
|
||||
* For a fascinating discussion of the best way to update the screen canvas at this point, see updateScreenGraphicsCGA().
|
||||
*/
|
||||
if (xDirty < this.nCols) {
|
||||
var cxDirty = xMaxDirty - xDirty;
|
||||
var cyDirty = yMaxDirty - yDirty;
|
||||
this.contextScreenBuffer.putImageData(this.imageScreenBuffer, 0, 0, xDirty, yDirty, cxDirty, cyDirty);
|
||||
this.contextScreen.drawImage(this.canvasScreenBuffer, 0, 0, this.nCols, this.nRows, 0, 0, this.cxScreen, this.cyScreen);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* getRetraceBits(card)
|
||||
*
|
||||
|
|
|
|||
131
tests/pc/vga/L42-1.C
Normal file
131
tests/pc/vga/L42-1.C
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
/* Function to draw an antialiased line from (X0,Y0) to (X1,Y1), using an
|
||||
* antialiasing approach published by Xiaolin Wu in the July 1991 issue of
|
||||
* Computer Graphics. Requires that the palette be set up so that there
|
||||
* are NumLevels intensity levels of the desired drawing color, starting at
|
||||
* color BaseColor (100% intensity) and followed by (NumLevels-1) levels of
|
||||
* evenly decreasing intensity, with color (BaseColor+NumLevels-1) being 0%
|
||||
* intensity of the desired drawing color (black). This code is suitable for
|
||||
* use at screen resolutions, with lines typically no more than 1K long; for
|
||||
* longer lines, 32-bit error arithmetic must be used to avoid problems with
|
||||
* fixed-point inaccuracy. No clipping is performed in DrawWuLine; it must be
|
||||
* performed either at a higher level or in the DrawPixel function.
|
||||
* Tested with Borland C++ in C compilation mode and the small model.
|
||||
*/
|
||||
extern void DrawPixel(int, int, int);
|
||||
|
||||
/* Wu antialiased line drawer.
|
||||
* (X0,Y0),(X1,Y1) = line to draw
|
||||
* BaseColor = color # of first color in block used for antialiasing, the
|
||||
* 100% intensity version of the drawing color
|
||||
* NumLevels = size of color block, with BaseColor+NumLevels-1 being the
|
||||
* 0% intensity version of the drawing color
|
||||
* IntensityBits = log base 2 of NumLevels; the # of bits used to describe
|
||||
* the intensity of the drawing color. 2**IntensityBits==NumLevels
|
||||
*/
|
||||
void DrawWuLine(int X0, int Y0, int X1, int Y1, int BaseColor, int NumLevels, unsigned int IntensityBits)
|
||||
{
|
||||
unsigned int IntensityShift, ErrorAdj, ErrorAcc;
|
||||
unsigned int ErrorAccTemp, Weighting, WeightingComplementMask;
|
||||
int DeltaX, DeltaY, Temp, XDir;
|
||||
|
||||
/* Make sure the line runs top to bottom */
|
||||
if (Y0 > Y1) {
|
||||
Temp = Y0; Y0 = Y1; Y1 = Temp;
|
||||
Temp = X0; X0 = X1; X1 = Temp;
|
||||
}
|
||||
/* Draw the initial pixel, which is always exactly intersected by
|
||||
the line and so needs no weighting */
|
||||
DrawPixel(X0, Y0, BaseColor);
|
||||
|
||||
if ((DeltaX = X1 - X0) >= 0) {
|
||||
XDir = 1;
|
||||
} else {
|
||||
XDir = -1;
|
||||
DeltaX = -DeltaX; /* make DeltaX positive */
|
||||
}
|
||||
/* Special-case horizontal, vertical, and diagonal lines, which
|
||||
require no weighting because they go right through the center of
|
||||
every pixel */
|
||||
if ((DeltaY = Y1 - Y0) == 0) {
|
||||
/* Horizontal line */
|
||||
while (DeltaX-- != 0) {
|
||||
X0 += XDir;
|
||||
DrawPixel(X0, Y0, BaseColor);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (DeltaX == 0) {
|
||||
/* Vertical line */
|
||||
do {
|
||||
Y0++;
|
||||
DrawPixel(X0, Y0, BaseColor);
|
||||
} while (--DeltaY != 0);
|
||||
return;
|
||||
}
|
||||
if (DeltaX == DeltaY) {
|
||||
/* Diagonal line */
|
||||
do {
|
||||
X0 += XDir;
|
||||
Y0++;
|
||||
DrawPixel(X0, Y0, BaseColor);
|
||||
} while (--DeltaY != 0);
|
||||
return;
|
||||
}
|
||||
/* Line is not horizontal, diagonal, or vertical */
|
||||
ErrorAcc = 0; /* initialize the line error accumulator to 0 */
|
||||
/* # of bits by which to shift ErrorAcc to get intensity level */
|
||||
IntensityShift = 16 - IntensityBits;
|
||||
/* Mask used to flip all bits in an intensity weighting, producing the
|
||||
result (1 - intensity weighting) */
|
||||
WeightingComplementMask = NumLevels - 1;
|
||||
/* Is this an X-major or Y-major line? */
|
||||
if (DeltaY > DeltaX) {
|
||||
/* Y-major line; calculate 16-bit fixed-point fractional part of a
|
||||
pixel that X advances each time Y advances 1 pixel, truncating the
|
||||
result so that we won't overrun the endpoint along the X axis */
|
||||
ErrorAdj = ((unsigned long) DeltaX << 16) / (unsigned long) DeltaY;
|
||||
/* Draw all pixels other than the first and last */
|
||||
while (--DeltaY) {
|
||||
ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */
|
||||
ErrorAcc += ErrorAdj; /* calculate error for next pixel */
|
||||
if (ErrorAcc <= ErrorAccTemp) {
|
||||
/* The error accumulator turned over, so advance the X coord */
|
||||
X0 += XDir;
|
||||
}
|
||||
Y0++; /* Y-major, so always advance Y */
|
||||
/* The IntensityBits most significant bits of ErrorAcc give us the
|
||||
intensity weighting for this pixel, and the complement of the
|
||||
weighting for the paired pixel */
|
||||
Weighting = ErrorAcc >> IntensityShift;
|
||||
DrawPixel(X0, Y0, BaseColor + Weighting);
|
||||
DrawPixel(X0 + XDir, Y0, BaseColor + (Weighting ^ WeightingComplementMask));
|
||||
}
|
||||
/* Draw the final pixel, which is always exactly intersected by the line
|
||||
and so needs no weighting */
|
||||
DrawPixel(X1, Y1, BaseColor);
|
||||
return;
|
||||
}
|
||||
/* It's an X-major line; calculate 16-bit fixed-point fractional part of a
|
||||
pixel that Y advances each time X advances 1 pixel, truncating the
|
||||
result to avoid overrunning the endpoint along the X axis */
|
||||
ErrorAdj = ((unsigned long) DeltaY << 16) / (unsigned long) DeltaX;
|
||||
/* Draw all pixels other than the first and last */
|
||||
while (--DeltaX) {
|
||||
ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */
|
||||
ErrorAcc += ErrorAdj; /* calculate error for next pixel */
|
||||
if (ErrorAcc <= ErrorAccTemp) {
|
||||
/* The error accumulator turned over, so advance the Y coord */
|
||||
Y0++;
|
||||
}
|
||||
X0 += XDir; /* X-major, so always advance X */
|
||||
/* The IntensityBits most significant bits of ErrorAcc give us the
|
||||
intensity weighting for this pixel, and the complement of the
|
||||
weighting for the paired pixel */
|
||||
Weighting = ErrorAcc >> IntensityShift;
|
||||
DrawPixel(X0, Y0, BaseColor + Weighting);
|
||||
DrawPixel(X0, Y0 + 1, BaseColor + (Weighting ^ WeightingComplementMask));
|
||||
}
|
||||
/* Draw the final pixel, which is always exactly intersected by the line
|
||||
and so needs no weighting */
|
||||
DrawPixel(X1, Y1, BaseColor);
|
||||
}
|
||||
124
tests/pc/vga/L42-2.C
Normal file
124
tests/pc/vga/L42-2.C
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/* Sample line-drawing program to demonstrate Wu antialiasing. Also draws
|
||||
* non-antialiased lines for comparison.
|
||||
* Tested with Borland C++ in C compilation mode and the small model.
|
||||
*/
|
||||
#include <dos.h>
|
||||
#include <conio.h>
|
||||
|
||||
void SetPalette(struct WuColor *);
|
||||
extern void DrawWuLine(int, int, int, int, int, int, unsigned int);
|
||||
extern void DrawLine(int, int, int, int, int);
|
||||
extern void SetMode(void);
|
||||
extern int ScreenWidthInPixels; /* screen dimension globals */
|
||||
extern int ScreenHeightInPixels;
|
||||
|
||||
#define NUM_WU_COLORS 2 /* # of colors we'll do antialiased drawing with */
|
||||
struct WuColor { /* describes one color used for antialiasing */
|
||||
int BaseColor; /* # of start of palette intensity block in DAC */
|
||||
int NumLevels; /* # of intensity levels */
|
||||
int IntensityBits; /* IntensityBits == log2 NumLevels */
|
||||
int MaxRed; /* red component of color at full intensity */
|
||||
int MaxGreen; /* green component of color at full intensity */
|
||||
int MaxBlue; /* blue component of color at full intensity */
|
||||
};
|
||||
enum {WU_BLUE=0, WU_WHITE=1}; /* drawing colors */
|
||||
struct WuColor WuColors[NUM_WU_COLORS] = /* blue and white */
|
||||
{{192, 32, 5, 0, 0, 0x3F}, {224, 32, 5, 0x3F, 0x3F, 0x3F}};
|
||||
|
||||
void main()
|
||||
{
|
||||
int CurrentColor, i;
|
||||
union REGS regset;
|
||||
|
||||
/* Draw Wu-antialiased lines in all directions */
|
||||
SetMode();
|
||||
SetPalette(WuColors);
|
||||
for (i=5; i<ScreenWidthInPixels; i += 10) {
|
||||
DrawWuLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
|
||||
ScreenHeightInPixels/5, i, ScreenHeightInPixels-1,
|
||||
WuColors[WU_BLUE].BaseColor, WuColors[WU_BLUE].NumLevels,
|
||||
WuColors[WU_BLUE].IntensityBits);
|
||||
}
|
||||
for (i=0; i<ScreenHeightInPixels; i += 10) {
|
||||
DrawWuLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10, i/5, 0, i,
|
||||
WuColors[WU_BLUE].BaseColor, WuColors[WU_BLUE].NumLevels,
|
||||
WuColors[WU_BLUE].IntensityBits);
|
||||
}
|
||||
for (i=0; i<ScreenHeightInPixels; i += 10) {
|
||||
DrawWuLine(ScreenWidthInPixels/2+ScreenWidthInPixels/10, i/5,
|
||||
ScreenWidthInPixels-1, i, WuColors[WU_BLUE].BaseColor,
|
||||
WuColors[WU_BLUE].NumLevels, WuColors[WU_BLUE].IntensityBits);
|
||||
}
|
||||
for (i=0; i<ScreenWidthInPixels; i += 10) {
|
||||
DrawWuLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
|
||||
ScreenHeightInPixels, i, 0, WuColors[WU_WHITE].BaseColor,
|
||||
WuColors[WU_WHITE].NumLevels,
|
||||
WuColors[WU_WHITE].IntensityBits);
|
||||
}
|
||||
getch(); /* wait for a key press */
|
||||
|
||||
/* Now clear the screen and draw non-antialiased lines */
|
||||
SetMode();
|
||||
SetPalette(WuColors);
|
||||
for (i=0; i<ScreenWidthInPixels; i += 10) {
|
||||
DrawLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
|
||||
ScreenHeightInPixels/5, i, ScreenHeightInPixels-1,
|
||||
WuColors[WU_BLUE].BaseColor);
|
||||
}
|
||||
for (i=0; i<ScreenHeightInPixels; i += 10) {
|
||||
DrawLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10, i/5, 0, i,
|
||||
WuColors[WU_BLUE].BaseColor);
|
||||
}
|
||||
for (i=0; i<ScreenHeightInPixels; i += 10) {
|
||||
DrawLine(ScreenWidthInPixels/2+ScreenWidthInPixels/10, i/5,
|
||||
ScreenWidthInPixels-1, i, WuColors[WU_BLUE].BaseColor);
|
||||
}
|
||||
for (i=0; i<ScreenWidthInPixels; i += 10) {
|
||||
DrawLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
|
||||
ScreenHeightInPixels, i, 0, WuColors[WU_WHITE].BaseColor);
|
||||
}
|
||||
getch(); /* wait for a key press */
|
||||
|
||||
regset.x.ax = 0x0003; /* AL = 3 selects 80x25 text mode */
|
||||
int86(0x10, ®set, ®set); /* return to text mode */
|
||||
}
|
||||
|
||||
/* Sets up the palette for antialiasing with the specified colors.
|
||||
* Intensity steps for each color are scaled from the full desired intensity
|
||||
* of the red, green, and blue components for that color down to 0%
|
||||
* intensity; each step is rounded to the nearest integer. Colors are
|
||||
* corrected for a gamma of 2.3. The values that the palette is programmed
|
||||
* with are hardwired for the VGA's 6 bit per color DAC.
|
||||
*/
|
||||
void SetPalette(struct WuColor * WColors)
|
||||
{
|
||||
int i, j;
|
||||
union REGS regset;
|
||||
struct SREGS sregset;
|
||||
static unsigned char PaletteBlock[256][3]; /* 256 RGB entries */
|
||||
/* Gamma-corrected DAC color components for 64 linear levels from 0% to 100% intensity */
|
||||
static unsigned char GammaTable[] = {
|
||||
0, 10, 14, 17, 19, 21, 23, 24, 26, 27, 28, 29, 31, 32, 33, 34,
|
||||
35, 36, 37, 37, 38, 39, 40, 41, 41, 42, 43, 44, 44, 45, 46, 46,
|
||||
47, 48, 48, 49, 49, 50, 51, 51, 52, 52, 53, 53, 54, 54, 55, 55,
|
||||
56, 56, 57, 57, 58, 58, 59, 59, 60, 60, 61, 61, 62, 62, 63, 63};
|
||||
|
||||
for (i=0; i<NUM_WU_COLORS; i++) {
|
||||
for (j=0; j<WColors[i].NumLevels; j++) {
|
||||
PaletteBlock[j][0] = GammaTable[((double)WColors[i].MaxRed * (1.0 -
|
||||
(double)j / (double)(WColors[i].NumLevels - 1))) + 0.5];
|
||||
PaletteBlock[j][1] = GammaTable[((double)WColors[i].MaxGreen * (1.0 -
|
||||
(double)j / (double)(WColors[i].NumLevels - 1))) + 0.5];
|
||||
PaletteBlock[j][2] = GammaTable[((double)WColors[i].MaxBlue * (1.0 -
|
||||
(double)j / (double)(WColors[i].NumLevels - 1))) + 0.5];
|
||||
}
|
||||
/* Now set up the palette to do Wu antialiasing for this color */
|
||||
regset.x.ax = 0x1012; /* set block of DAC registers function */
|
||||
regset.x.bx = WColors[i].BaseColor; /* first DAC location to load */
|
||||
regset.x.cx = WColors[i].NumLevels; /* # of DAC locations to load */
|
||||
regset.x.dx = (unsigned int)PaletteBlock; /* offset of array from which
|
||||
to load RGB settings */
|
||||
sregset.es = _DS; /* segment of array from which to load settings */
|
||||
int86x(0x10, ®set, ®set, &sregset); /* load the palette block */
|
||||
}
|
||||
}
|
||||
29
tests/pc/vga/L42-3.C
Normal file
29
tests/pc/vga/L42-3.C
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* VGA mode 13h pixel-drawing and mode set functions.
|
||||
* Tested with Borland C++ in C compilation mode and the small model.
|
||||
*/
|
||||
#include <dos.h>
|
||||
|
||||
/* Screen dimension globals, used in main program to scale. */
|
||||
int ScreenWidthInPixels = 320;
|
||||
int ScreenHeightInPixels = 200;
|
||||
|
||||
/* Mode 13h draw pixel function. */
|
||||
void DrawPixel(int X, int Y, int Color)
|
||||
{
|
||||
#define SCREEN_SEGMENT 0xA000
|
||||
unsigned char far *ScreenPtr;
|
||||
|
||||
FP_SEG(ScreenPtr) = SCREEN_SEGMENT;
|
||||
FP_OFF(ScreenPtr) = (unsigned int) Y * ScreenWidthInPixels + X;
|
||||
*ScreenPtr = Color;
|
||||
}
|
||||
|
||||
/* Mode 13h mode-set function. */
|
||||
void SetMode()
|
||||
{
|
||||
union REGS regset;
|
||||
|
||||
/* Set to 320x200 256-color graphics mode */
|
||||
regset.x.ax = 0x0013;
|
||||
int86(0x10, ®set, ®set);
|
||||
}
|
||||
64
tests/pc/vga/L42-4.C
Normal file
64
tests/pc/vga/L42-4.C
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/* Function to draw a non-antialiased line from (X0,Y0) to (X1,Y1), using a
|
||||
* simple fixed-point error accumulation approach.
|
||||
* Tested with Borland C++ in C compilation mode and the small model.
|
||||
*/
|
||||
extern void DrawPixel(int, int, int);
|
||||
|
||||
/* Non-antialiased line drawer.
|
||||
* (X0,Y0),(X1,Y1) = line to draw, Color = color in which to draw
|
||||
*/
|
||||
void DrawLine(int X0, int Y0, int X1, int Y1, int Color)
|
||||
{
|
||||
unsigned long ErrorAcc, ErrorAdj;
|
||||
int DeltaX, DeltaY, XDir, Temp;
|
||||
|
||||
/* Make sure the line runs top to bottom */
|
||||
if (Y0 > Y1) {
|
||||
Temp = Y0; Y0 = Y1; Y1 = Temp;
|
||||
Temp = X0; X0 = X1; X1 = Temp;
|
||||
}
|
||||
DrawPixel(X0, Y0, Color); /* draw the initial pixel */
|
||||
if ((DeltaX = X1 - X0) >= 0) {
|
||||
XDir = 1;
|
||||
} else {
|
||||
XDir = -1;
|
||||
DeltaX = -DeltaX; /* make DeltaX positive */
|
||||
}
|
||||
if ((DeltaY = Y1 - Y0) == 0) /* done if only one point in the line */
|
||||
if (DeltaX == 0) return;
|
||||
|
||||
ErrorAcc = 0x8000; /* initialize line error accumulator to .5, so we can
|
||||
advance when we get halfway to the next pixel */
|
||||
/* Is this an X-major or Y-major line? */
|
||||
if (DeltaY > DeltaX) {
|
||||
/* Y-major line; calculate 16-bit fixed-point fractional part of a
|
||||
pixel that X advances each time Y advances 1 pixel */
|
||||
ErrorAdj = ((((unsigned long)DeltaX << 17) / (unsigned long)DeltaY) + 1) >> 1;
|
||||
/* Draw all pixels between the first and last */
|
||||
do {
|
||||
ErrorAcc += ErrorAdj; /* calculate error for this pixel */
|
||||
if (ErrorAcc & ~0xFFFFL) {
|
||||
/* The error accumulator turned over, so advance the X coord */
|
||||
X0 += XDir;
|
||||
ErrorAcc &= 0xFFFFL; /* clear integer part of result */
|
||||
}
|
||||
Y0++; /* Y-major, so always advance Y */
|
||||
DrawPixel(X0, Y0, Color);
|
||||
} while (--DeltaY);
|
||||
return;
|
||||
}
|
||||
/* It's an X-major line; calculate 16-bit fixed-point fractional part of a
|
||||
pixel that Y advances each time X advances 1 pixel */
|
||||
ErrorAdj = ((((unsigned long)DeltaY << 17) / (unsigned long)DeltaX) + 1) >> 1;
|
||||
/* Draw all remaining pixels */
|
||||
do {
|
||||
ErrorAcc += ErrorAdj; /* calculate error for this pixel */
|
||||
if (ErrorAcc & ~0xFFFFL) {
|
||||
/* The error accumulator turned over, so advance the Y coord */
|
||||
Y0++;
|
||||
ErrorAcc &= 0xFFFFL; /* clear integer part of result */
|
||||
}
|
||||
X0 += XDir; /* X-major, so always advance X */
|
||||
DrawPixel(X0, Y0, Color);
|
||||
} while (--DeltaX);
|
||||
}
|
||||
40
tests/pc/vga/L42-5.C
Normal file
40
tests/pc/vga/L42-5.C
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/* Mode set and pixel-drawing functions for the 640x480 256-color mode of
|
||||
* Tseng Labs ET4000-based SuperVGAs.
|
||||
* Tested with Borland C++ in C compilation mode and the small model.
|
||||
*/
|
||||
#include <dos.h>
|
||||
|
||||
/* Screen dimension globals, used in main program to scale */
|
||||
int ScreenWidthInPixels = 640;
|
||||
int ScreenHeightInPixels = 480;
|
||||
|
||||
/* ET4000 640x480 256-color draw pixel function. */
|
||||
void DrawPixel(int X, int Y, int Color)
|
||||
{
|
||||
#define SCREEN_SEGMENT 0xA000
|
||||
#define GC_SEGMENT_SELECT 0x3CD /* ET4000 segment (bank) select reg */
|
||||
unsigned char far *ScreenPtr;
|
||||
unsigned int Bank;
|
||||
unsigned long BitmapAddress;
|
||||
|
||||
/* Full bitmap address of pixel, as measured from address 0 to 0xFFFFF */
|
||||
BitmapAddress = (unsigned long) Y * ScreenWidthInPixels + X;
|
||||
/* Bank # is upper word of bitmap addr */
|
||||
Bank = BitmapAddress >> 16;
|
||||
/* Upper nibble is read bank #, lower nibble is write bank # */
|
||||
outp(GC_SEGMENT_SELECT, (Bank << 4) | Bank);
|
||||
/* Draw into the bank */
|
||||
FP_SEG(ScreenPtr) = SCREEN_SEGMENT;
|
||||
FP_OFF(ScreenPtr) = (unsigned int) BitmapAddress;
|
||||
*ScreenPtr = Color;
|
||||
}
|
||||
|
||||
/* ET4000 640x480 256-color mode-set function. */
|
||||
void SetMode()
|
||||
{
|
||||
union REGS regset;
|
||||
|
||||
/* Set to 640x480 256-color graphics mode */
|
||||
regset.x.ax = 0x002E;
|
||||
int86(0x10, ®set, ®set);
|
||||
}
|
||||
270
tests/pc/vga/L42-6.ASM
Normal file
270
tests/pc/vga/L42-6.ASM
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
; C near-callable function to draw an antialiased line from
|
||||
; (X0,Y0) to (X1,Y1), in mode 13h, the VGA's standard 320x200 256-color
|
||||
; mode. Uses an antialiasing approach published by Xiaolin Wu in the July
|
||||
; 1991 issue of Computer Graphics. Requires that the palette be set up so
|
||||
; that there are NumLevels intensity levels of the desired drawing color,
|
||||
; starting at color BaseColor (100% intensity) and followed by (NumLevels-1)
|
||||
; levels of evenly decreasing intensity, with color (BaseColor+NumLevels-1)
|
||||
; being 0% intensity of the desired drawing color (black). No clipping is
|
||||
; performed in DrawWuLine. Handles a maximum of 256 intensity levels per
|
||||
; antialiased color. This code is suitable for use at screen resolutions,
|
||||
; with lines typically no more than 1K long; for longer lines, 32-bit error
|
||||
; arithmetic must be used to avoid problems with fixed-point inaccuracy.
|
||||
; Tested with TASM.
|
||||
;
|
||||
; C near-callable as:
|
||||
; void DrawWuLine(int X0, int Y0, int X1, int Y1, int BaseColor,
|
||||
; int NumLevels, unsigned int IntensityBits);
|
||||
|
||||
SCREEN_WIDTH_IN_BYTES equ 320 ;# of bytes from the start of one scan line
|
||||
; to the start of the next
|
||||
SCREEN_SEGMENT equ 0a000h ;segment in which screen memory resides
|
||||
|
||||
; Parameters passed in stack frame.
|
||||
parms struc
|
||||
dw 2 dup (?) ;pushed BP and return address
|
||||
X0 dw ? ;X coordinate of line start point
|
||||
Y0 dw ? ;Y coordinate of line start point
|
||||
X1 dw ? ;X coordinate of line end point
|
||||
Y1 dw ? ;Y coordinate of line end point
|
||||
BaseColor dw ? ;color # of first color in block used for
|
||||
;antialiasing, the 100% intensity version of the
|
||||
;drawing color
|
||||
NumLevels dw ? ;size of color block, with BaseColor+NumLevels-1
|
||||
; being the 0% intensity version of the drawing color
|
||||
; (maximum NumLevels = 256)
|
||||
IntensityBits dw ? ;log base 2 of NumLevels; the # of bits used to
|
||||
; describe the intensity of the drawing color.
|
||||
; 2**IntensityBits==NumLevels
|
||||
; (maximum IntensityBits = 8)
|
||||
parms ends
|
||||
|
||||
.model small
|
||||
.code
|
||||
; Screen dimension globals, used in main program to scale.
|
||||
_ScreenWidthInPixels dw 320
|
||||
_ScreenHeightInPixels dw 200
|
||||
|
||||
.code
|
||||
public _DrawWuLine
|
||||
_DrawWuLine proc near
|
||||
push bp ;preserve caller's stack frame
|
||||
mov bp,sp ;point to local stack frame
|
||||
push si ;preserve C's register variables
|
||||
push di
|
||||
push ds ;preserve C's default data segment
|
||||
cld ;make string instructions increment their pointers
|
||||
|
||||
; Make sure the line runs top to bottom.
|
||||
mov si,[bp].X0
|
||||
mov ax,[bp].Y0
|
||||
cmp ax,[bp].Y1 ;swap endpoints if necessary to ensure that
|
||||
jna NoSwap ; Y0 <= Y1
|
||||
xchg [bp].Y1,ax
|
||||
mov [bp].Y0,ax
|
||||
xchg [bp].X1,si
|
||||
mov [bp].X0,si
|
||||
NoSwap:
|
||||
|
||||
; Draw the initial pixel, which is always exactly intersected by the line
|
||||
; and so needs no weighting.
|
||||
mov dx,SCREEN_SEGMENT
|
||||
mov ds,dx ;point DS to the screen segment
|
||||
mov dx,SCREEN_WIDTH_IN_BYTES
|
||||
mul dx ;Y0 * SCREEN_WIDTH_IN_BYTES yields the offset
|
||||
; of the start of the row start the initial
|
||||
; pixel is on
|
||||
add si,ax ;point DS:SI to the initial pixel
|
||||
mov al,byte ptr [bp].BaseColor ;color with which to draw
|
||||
mov [si],al ;draw the initial pixel
|
||||
|
||||
mov bx,1 ;XDir = 1; assume DeltaX >= 0
|
||||
mov cx,[bp].X1
|
||||
sub cx,[bp].X0 ;DeltaX; is it >= 1?
|
||||
jns DeltaXSet ;yes, move left->right, all set
|
||||
;no, move right->left
|
||||
neg cx ;make DeltaX positive
|
||||
neg bx ;XDir = -1
|
||||
DeltaXSet:
|
||||
|
||||
; Special-case horizontal, vertical, and diagonal lines, which require no
|
||||
; weighting because they go right through the center of every pixel.
|
||||
mov dx,[bp].Y1
|
||||
sub dx,[bp].Y0 ;DeltaY; is it 0?
|
||||
jnz NotHorz ;no, not horizontal
|
||||
;yes, is horizontal, special case
|
||||
and bx,bx ;draw from left->right?
|
||||
jns DoHorz ;yes, all set
|
||||
std ;no, draw right->left
|
||||
DoHorz:
|
||||
lea di,[bx+si] ;point DI to next pixel to draw
|
||||
mov ax,ds
|
||||
mov es,ax ;point ES:DI to next pixel to draw
|
||||
mov al,byte ptr [bp].BaseColor ;color with which to draw
|
||||
;CX = DeltaX at this point
|
||||
rep stosb ;draw the rest of the horizontal line
|
||||
cld ;restore default direction flag
|
||||
jmp Done ;and we're done
|
||||
|
||||
align 2
|
||||
NotHorz:
|
||||
and cx,cx ;is DeltaX 0?
|
||||
jnz NotVert ;no, not a vertical line
|
||||
;yes, is vertical, special case
|
||||
mov al,byte ptr [bp].BaseColor ;color with which to draw
|
||||
VertLoop:
|
||||
add si,SCREEN_WIDTH_IN_BYTES ;point to next pixel to draw
|
||||
mov [si],al ;draw the next pixel
|
||||
dec dx ;--DeltaY
|
||||
jnz VertLoop
|
||||
jmp Done ;and we're done
|
||||
|
||||
align 2
|
||||
NotVert:
|
||||
cmp cx,dx ;DeltaX == DeltaY?
|
||||
jnz NotDiag ;no, not diagonal
|
||||
;yes, is diagonal, special case
|
||||
mov al,byte ptr [bp].BaseColor ;color with which to draw
|
||||
DiagLoop:
|
||||
lea si,[si+SCREEN_WIDTH_IN_BYTES+bx]
|
||||
;advance to next pixel to draw by
|
||||
; incrementing Y and adding XDir to X
|
||||
mov [si],al ;draw the next pixel
|
||||
dec dx ;--DeltaY
|
||||
jnz DiagLoop
|
||||
jmp Done ;and we're done
|
||||
|
||||
; Line is not horizontal, diagonal, or vertical.
|
||||
align 2
|
||||
NotDiag:
|
||||
; Is this an X-major or Y-major line?
|
||||
cmp dx,cx
|
||||
jb XMajor ;it's X-major
|
||||
|
||||
; It's a Y-major line. Calculate the 16-bit fixed-point fractional part of a
|
||||
; pixel that X advances each time Y advances 1 pixel, truncating the result
|
||||
; to avoid overrunning the endpoint along the X axis.
|
||||
xchg dx,cx ;DX = DeltaX, CX = DeltaY
|
||||
sub ax,ax ;make DeltaX 16.16 fixed-point value in DX:AX
|
||||
div cx ;AX = (DeltaX << 16) / DeltaY. Won't overflow
|
||||
; because DeltaX < DeltaY
|
||||
mov di,cx ;DI = DeltaY (loop count)
|
||||
sub si,bx ;back up the start X by 1, as explained below
|
||||
mov dx,-1 ;initialize the line error accumulator to -1,
|
||||
; so that it will turn over immediately and
|
||||
; advance X to the start X. This is necessary
|
||||
; properly to bias error sums of 0 to mean
|
||||
; "advance next time" rather than "advance
|
||||
; this time," so that the final error sum can
|
||||
; never cause drawing to overrun the final X
|
||||
; coordinate (works in conjunction with
|
||||
; truncating ErrorAdj, to make sure X can't
|
||||
; overrun)
|
||||
mov cx,8 ;CL = # of bits by which to shift
|
||||
sub cx,[bp].IntensityBits ; ErrorAcc to get intensity level (8
|
||||
; instead of 16 because we work only
|
||||
; with the high byte of ErrorAcc)
|
||||
mov ch,byte ptr [bp].NumLevels ;mask used to flip all bits in an
|
||||
dec ch ; intensity weighting, producing
|
||||
; result (1 - intensity weighting)
|
||||
mov bp,BaseColor[bp] ;***stack frame not available***
|
||||
;***from now on ***
|
||||
xchg bp,ax ;BP = ErrorAdj, AL = BaseColor,
|
||||
; AH = scratch register
|
||||
|
||||
; Draw all remaining pixels.
|
||||
YMajorLoop:
|
||||
add dx,bp ;calculate error for next pixel
|
||||
jnc NoXAdvance ;not time to step in X yet
|
||||
;the error accumulator turned over,
|
||||
;so advance the X coord
|
||||
add si,bx ;add XDir to the pixel pointer
|
||||
NoXAdvance:
|
||||
add si,SCREEN_WIDTH_IN_BYTES ;Y-major, so always advance Y
|
||||
|
||||
; The IntensityBits most significant bits of ErrorAcc give us the intensity
|
||||
; weighting for this pixel, and the complement of the weighting for the
|
||||
; paired pixel.
|
||||
mov ah,dh ;msb of ErrorAcc
|
||||
shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
|
||||
add ah,al ;BaseColor + Weighting
|
||||
mov [si],ah ;DrawPixel(X, Y, BaseColor + Weighting);
|
||||
mov ah,dh ;msb of ErrorAcc
|
||||
shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
|
||||
xor ah,ch ;Weighting ^ WeightingComplementMask
|
||||
add ah,al ;BaseColor + (Weighting ^ WeightingComplementMask)
|
||||
mov [si+bx],ah ;DrawPixel(X+XDir, Y,
|
||||
; BaseColor + (Weighting ^ WeightingComplementMask));
|
||||
dec di ;--DeltaY
|
||||
jnz YMajorLoop
|
||||
jmp Done ;we're done with this line
|
||||
|
||||
; It's an X-major line.
|
||||
align 2
|
||||
XMajor:
|
||||
; Calculate the 16-bit fixed-point fractional part of a pixel that Y advances
|
||||
; each time X advances 1 pixel, truncating the result to avoid overrunning
|
||||
; the endpoint along the X axis.
|
||||
sub ax,ax ;make DeltaY 16.16 fixed-point value in DX:AX
|
||||
div cx ;AX = (DeltaY << 16) / Deltax. Won't overflow
|
||||
; because DeltaY < DeltaX
|
||||
mov di,cx ;DI = DeltaX (loop count)
|
||||
sub si,SCREEN_WIDTH_IN_BYTES;back up the start X by 1, as
|
||||
; explained below
|
||||
mov dx,-1 ;initialize the line error accumulator to -1,
|
||||
; so that it will turn over immediately and
|
||||
; advance Y to the start Y. This is necessary
|
||||
; properly to bias error sums of 0 to mean
|
||||
; "advance next time" rather than "advance
|
||||
; this time," so that the final error sum can
|
||||
; never cause drawing to overrun the final Y
|
||||
; coordinate (works in conjunction with
|
||||
; truncating ErrorAdj, to make sure Y can't
|
||||
; overrun)
|
||||
mov cx,8 ;CL = # of bits by which to shift
|
||||
sub cx,[bp].IntensityBits ; ErrorAcc to get intensity level (8
|
||||
; instead of 16 because we work only
|
||||
; with the high byte of ErrorAcc)
|
||||
mov ch,byte ptr [bp].NumLevels ;mask used to flip all bits in an
|
||||
dec ch ; intensity weighting, producing
|
||||
; result (1 - intensity weighting)
|
||||
mov bp,BaseColor[bp] ;***stack frame not available***
|
||||
;***from now on ***
|
||||
xchg bp,ax ;BP = ErrorAdj, AL = BaseColor,
|
||||
; AH = scratch register
|
||||
; Draw all remaining pixels.
|
||||
XMajorLoop:
|
||||
add dx,bp ;calculate error for next pixel
|
||||
jnc NoYAdvance ;not time to step in Y yet
|
||||
;the error accumulator turned over,
|
||||
; so advance the Y coord
|
||||
add si,SCREEN_WIDTH_IN_BYTES;advance Y
|
||||
NoYAdvance:
|
||||
add si,bx ;X-major, so add XDir to the pixel pointer
|
||||
|
||||
; The IntensityBits most significant bits of ErrorAcc give us the intensity
|
||||
; weighting for this pixel, and the complement of the weighting for the
|
||||
; paired pixel.
|
||||
mov ah,dh ;msb of ErrorAcc
|
||||
shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
|
||||
add ah,al ;BaseColor + Weighting
|
||||
mov [si],ah ;DrawPixel(X, Y, BaseColor + Weighting);
|
||||
mov ah,dh ;msb of ErrorAcc
|
||||
shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
|
||||
xor ah,ch ;Weighting ^ WeightingComplementMask
|
||||
add ah,al ;BaseColor + (Weighting ^ WeightingComplementMask)
|
||||
mov [si+SCREEN_WIDTH_IN_BYTES],ah
|
||||
|
||||
; DrawPixel(X, Y+SCREEN_WIDTH_IN_BYTES, BaseColor + (Weighting ^ WeightingComplementMask));
|
||||
dec di ;--DeltaX
|
||||
jnz XMajorLoop
|
||||
|
||||
Done: ;we're done with this line
|
||||
pop ds ;restore C's default data segment
|
||||
pop di ;restore C's register variables
|
||||
pop si
|
||||
pop bp ;restore caller's stack frame
|
||||
ret ;done
|
||||
_DrawWuLine endp
|
||||
end
|
||||
|
||||
Loading…
Reference in a new issue