Mode X test

This commit is contained in:
Jeff Parsons 2015-07-06 16:18:27 -07:00
commit 8e09ad0bff
10 changed files with 671 additions and 73 deletions

View file

@ -507,14 +507,15 @@ Video.MODE = {
CGA_320X200_BW: 5,
CGA_640X200: 6,
MDA_80X25: 7,
EGA_320X200: 0x0D, // mapped at A000:0000
EGA_640X200: 0x0E, // mapped at A000:0000
EGA_640X350_MONO: 0x0F, // mapped at A000:0000, monochrome
EGA_640X350: 0x10, // mapped at A000:0000, color
VGA_640X480_MONO: 0x11, // mapped at A000:0000, monochrome
VGA_640X480: 0x12, // mapped at A000:0000, color
VGA_320X200: 0x13, // mapped at A000:0000, color
VGA_320X400: 0x26,
EGA_320X200: 0x0D, // mapped at A000:0000, color, 4bpp, planar
EGA_640X200: 0x0E, // mapped at A000:0000, color, 4bpp, planar
EGA_640X350_MONO: 0x0F, // mapped at A000:0000, mono, 2bpp, planar
EGA_640X350: 0x10, // mapped at A000:0000, color, 4bpp, planar
VGA_640X480_MONO: 0x11, // mapped at A000:0000, mono, 2bpp, planar
VGA_640X480: 0x12, // mapped at A000:0000, color, 4bpp, planar
VGA_320X200: 0x13, // mapped at A000:0000, color, 8bpp, linear
VGA_320X240: 0x78, // mapped at A000:0000, color, 8bpp, planar ("Mode X")
VGA_320X400: 0x7A, // mapped at A000:0000, color, 8bpp, planar
UNKNOWN: 0xFF
};
@ -636,8 +637,8 @@ Video.monitorSpecs[ChipSet.MONITOR.VGACOLOR] = {
* 0x0: return SW4
*
* These 4 bits are also copied to the byte at 40:88h by the EGA BIOS, where bit 0 is SW1, bit 1 is SW2,
* bit 2 is SW3 and bit 3 is SW4. Our switch settings come from bEGASwitches, which in turn comes from sSwitches,
* which in turn comes from the "switches" property passed to the Video component, if any.
* bit 2 is SW3 and bit 3 is SW4. Our switch settings come from bEGASwitches, which in turn comes from
* sSwitches, which in turn comes from the "switches" property passed to the Video component, if any.
*
* As usual, the switch settings are reversed in both direction and sense from the switch settings; the
* good news, however, is that we can use the parseSwitches() method in the ChipSet component to parse them.
@ -675,7 +676,7 @@ Video.aEGAMonitorSwitches = {
* Supported Fonts
*
* Once we've finished loading the standard 8K font file, aFonts[] should contain one or more of the
* fonts listed below. For the standard MDA/CGA font ROM, the first (MDA) font resides in the first 4Kb,
* entries listed below. For the standard MDA/CGA font ROM, the first (MDA) font resides in the first 4Kb,
* and the second and third (CGA) fonts reside in the two 2K halves of the second 4Kb.
*
* It may seem odd that the cell size for FONT_CGAD is *larger* than the cell size for FONT_CGA,
@ -707,12 +708,16 @@ Video.FONT = {
*
* 0: # of columns (nCols)
* 1: # of rows (nRows)
* 2: # cells per word (nCellsPerWord: # of characters or pixels per 16-bit word)
* 2: # cells per word (nCellsPerWord: # of characters or pixels per word)
* 3: # bytes of visible screen padding, if any (used for CGA graphics modes only)
* 4: font ID (nFont: undefined if graphics mode)
*
* By calculating ([0] * [1]) / [2], we obtain the number of 16-bit words that mode actively displays;
* for example, the amount of visible memory used by mode 0x04 is (320 * 200) / 4, or 16000.
* For MDA and CGA modes, a "word" of memory is 16 bits of CPU-addressable data, so by calculating
* ([0] * [1]) / [2], we obtain the number of words that mode actively displays; for example, the
* amount of visible memory used by mode 0x04 is (320 * 200) / 4, or 16000.
*
* However, for EGA and VGA graphics modes, a "word" of memory is a single element in the video buffer
* containing 32 bits of pixel data.
*
* The MODES.CGA_40X25 modes specify FONT_CGA instead of FONT_CGAD because we don't automatically
* load the FONT_CGAD unless the screen is large enough to accommodate it (see the fDoubleFont calculation).
@ -727,14 +732,15 @@ Video.aModeParms[Video.MODE.CGA_80X25] = [ 80, 25, 1, 0, Video.FONT
Video.aModeParms[Video.MODE.CGA_320X200] = [320, 200, 8, 192]; // 0x04
Video.aModeParms[Video.MODE.CGA_640X200] = [640, 200, 16, 192]; // 0x06
Video.aModeParms[Video.MODE.MDA_80X25] = [ 80, 25, 1, 0, Video.FONT.MDA]; // 0x07
Video.aModeParms[Video.MODE.EGA_320X200] = [320, 200, 16]; // 0x0D
Video.aModeParms[Video.MODE.EGA_640X200] = [640, 200, 16]; // 0x0E
Video.aModeParms[Video.MODE.EGA_640X350_MONO] = [640, 350, 16]; // 0x0F
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, 2]; // 0x13
Video.aModeParms[Video.MODE.VGA_320X400] = [320, 400, 8]; // 0x26
Video.aModeParms[Video.MODE.EGA_320X200] = [320, 200, 8]; // 0x0D
Video.aModeParms[Video.MODE.EGA_640X200] = [640, 200, 8]; // 0x0E
Video.aModeParms[Video.MODE.EGA_640X350_MONO] = [640, 350, 8]; // 0x0F
Video.aModeParms[Video.MODE.EGA_640X350] = [640, 350, 8]; // 0x10
Video.aModeParms[Video.MODE.VGA_640X480_MONO] = [640, 480, 8]; // 0x11
Video.aModeParms[Video.MODE.VGA_640X480] = [640, 480, 8]; // 0x12
Video.aModeParms[Video.MODE.VGA_320X200] = [320, 200, 1]; // 0x13
Video.aModeParms[Video.MODE.VGA_320X240] = [320, 240, 4]; // 0x78
Video.aModeParms[Video.MODE.VGA_320X400] = [320, 400, 4]; // 0x7A
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
@ -3577,7 +3583,6 @@ Video.prototype.getCardColors = function(nBitsPerPixel)
this.assert(this.cardColor === this.cardEGA);
if (this.fRGBValid && nBitsPerPixel && !this.aRGB[16]) {
this.assert(false);
this.fRGBValid = false;
}
@ -3617,7 +3622,7 @@ Video.prototype.getCardColors = function(nBitsPerPixel)
* for bits 6 and 7 from the ATC COLORSEL register (and overwrite bits 4 and 5 if ATC.MODE.COLORSEL_ALL
* is set as well).
*
* The only reason the DAC wouldn't be valid is if 1) we're trying to display an image before the machine
* The only reasons the DAC wouldn't be valid are if 1) we're trying to display an image before the machine
* and its BIOS have had a chance to initialize the DAC (because we don't preset it to anything, although
* perhaps we should), or 2) this is an EGA, which doesn't have a DAC.
*/
@ -4274,15 +4279,15 @@ Video.prototype.setDimensions = function()
this.nColsLogical = this.nCols;
this.nCellsPerWord = Video.aModeParms[Video.MODE.MDA_80X25][2];
this.cbPadding = 0;
var cbPadding = 0;
var modeParms = Video.aModeParms[this.nMode];
if (modeParms) {
this.nCols = modeParms[0];
this.nRows = modeParms[1];
this.nCellsPerWord = modeParms[2];
this.cbPadding = modeParms[3] || 0;
this.nFont = modeParms[4]; // this will be undefined for graphics modes
cbPadding = modeParms[3]; // undefined for EGA/VGA graphics modes only
this.nFont = modeParms[4]; // this will be undefined for all graphics modes
if (this.nMonitorType == ChipSet.MONITOR.EGACOLOR || this.nMonitorType == ChipSet.MONITOR.VGACOLOR) {
/*
@ -4319,9 +4324,13 @@ Video.prototype.setDimensions = function()
this.nCells = (this.nCols * this.nRows)|0;
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);
if (this.nMode >= Video.MODE.EGA_320X200) this.nCellCache <<= 1; // double nCellCache (every cell is a byte)
this.cbScreen = this.nCellCache;
this.cbSplit = 0;
if (cbPadding !== undefined) {
this.cbScreen = ((this.cbScreen << 1) + cbPadding)|0;
this.cbSplit = (this.cbScreen + cbPadding) >> 1;
}
/*
* If no fonts were successfully loaded, there's no point in initializing the remaining drawing parameters.
@ -4530,13 +4539,19 @@ Video.prototype.checkMode = function(fForce)
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).
* card.addrBuffer must be 0xA0000, so we need to discriminate between modes 0x0D and up;
* 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.regGRCData[Card.GRC.MODE.INDX] & Card.GRC.MODE.COLOR256) {
nMode = Video.MODE.VGA_320X200;
if (!(card.regCRTData[Card.CRTC.MAX_SCAN.INDX] & Card.CRTC.MAX_SCAN.SCAN_LINE)) {
if (card.regCRTData[Card.CRTC.MAX_SCAN.INDX] & Card.CRTC.MAX_SCAN.SCAN_LINE) {
if (card.regCRTData[Card.CRTC.EGA.VDISP_END] <= 0x8F) {
nMode = Video.MODE.VGA_320X200;
}
else { /* (card.regCRTData[Card.CRTC.EGA.VDISP_END] == 0xDF) */
nMode = Video.MODE.VGA_320X240;
}
} else {
nMode = Video.MODE.VGA_320X400;
}
}
@ -4672,7 +4687,7 @@ Video.prototype.setMode = function(nMode, fForce)
if (fForce !== false) {
this.updateScreen(true);
} else {
this.initCellCache(true);
this.invalidateScreen(true);
}
}
return true;
@ -4714,14 +4729,28 @@ Video.prototype.initCellCache = function(fNew)
var nCells = this.nCellCache;
if (this.aCellCache === undefined || this.aCellCache.length != nCells) {
this.aCellCache = new Array(nCells);
/*
* TODO: Determine whether, with the introduction of fCellCacheValid, this array initialization is useful.
*/
for (var iCell = 0; iCell < nCells; iCell++) this.aCellCache[iCell] = -1;
}
}
};
/**
* invalidateScreen(fNew)
*
* Ensure that the next updateScreen() will update every cell; intended for situations where the entire screen needs
* to be redrawn, even though the underlying data in the video buffer has not changed (and therefore cleanMemory() will
* report that the buffer is still clean, and/or all the video data still matches everything in our cell cache).
*
* For example, when the palette is being cycled, the screen is being panned, the page is being flipped, etc.
*
* @this {Video}
* @param {boolean} [fNew] is passed through to initCellCache; typically true when a new mode has been set.
*/
Video.prototype.invalidateScreen = function(fNew)
{
this.fRGBValid = false;
this.initCellCache(fNew);
};
/**
* doBlink()
*
@ -4976,7 +5005,7 @@ Video.prototype.updateScreen = function(fForce)
* 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);
cbScreen = (((this.nColsLogical * (this.nRows-1) + this.nCols) / this.nCellsPerWord) << 1)|0;
cbScreen = ((this.nColsLogical * (this.nRows-1) + this.nCols) / this.nCellsPerWord)|0;
}
if (addrScreen + cbScreen > addrScreenLimit) {
@ -5005,7 +5034,7 @@ Video.prototype.updateScreen = function(fForce)
* that small optimization off. Further optimizations are certainly possible, such as a hash table
* of all blinking character locations, but all those optimizations are saved for a rainy day.
*/
if (!fForce && this.bus.cleanMemory(addrScreen, cbScreen)) {
if (!fForce && this.fCellCacheValid && this.bus.cleanMemory(addrScreen, cbScreen)) {
if (!fBlinkUpdate) return;
if (!this.cBlinkVisible) {
if (this.iCellCursor < 0) return;
@ -5302,10 +5331,10 @@ Video.prototype.updateScreenGraphicsEGA = function(addrScreen, addrScreenLimit)
/**
* updateScreenGraphicsVGA(addrScreen, addrScreenLimit)
*
* 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), where each pixel's bits are spread across multiple
* planes, whereas this function takes care of just the "linear" video modes introduced by the VGA, such as mode 0x13
* (320x200x256), where each pixel's bits are contained within a single plane. This is basically all 256-color 8bpp
* This function name is a slight misnomer: updateScreenGraphicsEGA() takes care of all the 4bpp video modes
* (first introduced by the EGA and later expanded by the VGA), where each pixel's bits are spread across the 4
* planes, whereas this function takes care of just the 8bpp video modes introduced by the VGA, such as mode 0x13
* (320x200x256), where each pixel's bits are contained within a single plane. This is essentially all 256-color
* (CHAIN4, CHAIN1, etc) modes, hence the hard-coded call to getCardColors(8).
*
* @param addrScreen
@ -5588,11 +5617,12 @@ Video.prototype.inATC = function(port, addrFrom)
*/
Video.prototype.outATC = function(port, bOut, addrFrom)
{
var fPalEnabled = (this.cardEGA.regATCIndx & Card.ATC.INDX_PAL_ENABLE);
if (!this.cardEGA.fATCData) {
this.cardEGA.regATCIndx = bOut;
var card = this.cardEGA;
var fPalEnabled = (card.regATCIndx & Card.ATC.INDX_PAL_ENABLE);
if (!card.fATCData) {
card.regATCIndx = bOut;
this.printMessageIO(port, bOut, addrFrom, "ATC.INDX");
this.cardEGA.fATCData = true;
card.fATCData = true;
if ((bOut & Card.ATC.INDX_PAL_ENABLE) && !fPalEnabled) {
if (!this.buildFonts()) {
if (DEBUG && (!addrFrom || this.messageEnabled())) {
@ -5602,7 +5632,6 @@ Video.prototype.outATC = function(port, bOut, addrFrom)
if (DEBUG && (!addrFrom || this.messageEnabled())) {
this.printMessage("outATC(" + str.toHexByte(bOut) + "): redraw screen for font changes");
}
this.fRGBValid = false;
this.updateScreen(true);
}
}
@ -5610,19 +5639,19 @@ Video.prototype.outATC = function(port, bOut, addrFrom)
* 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.
*/
this.cardEGA.nStartAddress = ((this.cardEGA.regCRTData[Card.CRTC.START_ADDR_HI] << 8) + this.cardEGA.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;
} else {
var iReg = this.cardEGA.regATCIndx & Card.ATC.INDX_MASK;
card.fATCData = false;
var iReg = card.regATCIndx & Card.ATC.INDX_MASK;
if (iReg >= Card.ATC.PALETTE_REGS || !fPalEnabled) {
if (Video.TRAPALL || this.cardEGA.regATCData[iReg] !== bOut) {
if (Video.TRAPALL || card.regATCData[iReg] !== bOut) {
if (!addrFrom || this.messageEnabled()) {
this.printMessageIO(port, bOut, addrFrom, "ATC." + this.cardEGA.asATCRegs[iReg]);
this.printMessageIO(port, bOut, addrFrom, "ATC." + card.asATCRegs[iReg]);
}
this.cardEGA.regATCData[iReg] = bOut;
this.fRGBValid = false;
card.regATCData[iReg] = bOut;
this.invalidateScreen();
}
}
this.cardEGA.fATCData = false;
}
};
@ -5928,13 +5957,16 @@ Video.prototype.outDACData = function(port, bOut, addrFrom)
if (!addrFrom || this.messageEnabled()) {
this.printMessageIO(Card.DAC.DATA.PORT, bOut, addrFrom, "DAC.DATA[" + str.toHexByte(this.cardEGA.regDACAddr) + "][" + str.toHexByte(this.cardEGA.regDACShift) + "]");
}
this.cardEGA.regDACData[this.cardEGA.regDACAddr] = (dw & ~(0x3f << this.cardEGA.regDACShift)) | ((bOut & 0x3f) << this.cardEGA.regDACShift);
var dwNew = (dw & ~(0x3f << this.cardEGA.regDACShift)) | ((bOut & 0x3f) << this.cardEGA.regDACShift);
if (dw !== dwNew) {
this.cardEGA.regDACData[this.cardEGA.regDACAddr] = dwNew;
this.invalidateScreen();
}
this.cardEGA.regDACShift += 6;
if (this.cardEGA.regDACShift > 12) {
this.cardEGA.regDACShift = 0;
this.cardEGA.regDACAddr = (this.cardEGA.regDACAddr + 1) & (Card.DAC.TOTAL_REGS-1);
}
this.fRGBValid = false;
};
/**

88
tests/pc/vga/L47-1.ASM Normal file
View file

@ -0,0 +1,88 @@
; Mode X (320x240, 256 colors) mode set routine. Works on all VGAs.
; ****************************************************************
; * Revised 6/19/91 to select correct clock; fixes vertical roll *
; * problems on fixed-frequency (IBM 851X-type) monitors. *
; ****************************************************************
; C near-callable as:
; void Set320x240Mode(void);
; Tested with TASM
; Modified from public-domain mode set code by John Bridges.
SC_INDEX equ 03c4h ;Sequence Controller Index
CRTC_INDEX equ 03d4h ;CRT Controller Index
MISC_OUTPUT equ 03c2h ;Miscellaneous Output register
SCREEN_SEG equ 0a000h ;segment of display memory in mode X
.model small
.data
; Index/data pairs for CRT Controller registers that differ between
; mode 13h and mode X.
CRTParms label word
dw 00d06h ;vertical total
dw 03e07h ;overflow (bit 8 of vertical counts)
dw 04109h ;cell height (2 to double-scan)
dw 0ea10h ;v sync start
dw 0ac11h ;v sync end and protect cr0-cr7
dw 0df12h ;vertical displayed
dw 00014h ;turn off dword mode
dw 0e715h ;v blank start
dw 00616h ;v blank end
dw 0e317h ;turn on byte mode
CRT_PARM_LENGTH equ (($-CRTParms)/2)
.code
public _Set320x240Mode
_Set320x240Mode proc near
push bp ;preserve caller's stack frame
push si ;preserve C register vars
push di ; (don't count on BIOS preserving anything)
mov ax,13h ;let the BIOS set standard 256-color
int 10h ; mode (320x200 linear)
mov dx,SC_INDEX
mov ax,0604h
out dx,ax ;disable chain4 mode
mov ax,0100h
out dx,ax ;synchronous reset while setting Misc Output
; for safety, even though clock unchanged
mov dx,MISC_OUTPUT
mov al,0e3h
out dx,al ;select 25 MHz dot clock & 60 Hz scanning rate
mov dx,SC_INDEX
mov ax,0300h
out dx,ax ;undo reset (restart sequencer)
mov dx,CRTC_INDEX ;reprogram the CRT Controller
mov al,11h ;VSync End reg contains register write
out dx,al ; protect bit
inc dx ;CRT Controller Data register
in al,dx ;get current VSync End register setting
and al,7fh ;remove write protect on various
out dx,al ; CRTC registers
dec dx ;CRT Controller Index
cld
mov si,offset CRTParms ;point to CRT parameter table
mov cx,CRT_PARM_LENGTH ;# of table entries
SetCRTParmsLoop:
lodsw ;get the next CRT Index/Data pair
out dx,ax ;set the next CRT Index/Data pair
loop SetCRTParmsLoop
mov dx,SC_INDEX
mov ax,0f02h
out dx,ax ;enable writes to all four planes
mov ax,SCREEN_SEG ;now clear all display memory, 8 pixels
mov es,ax ; at a time
sub di,di ;point ES:DI to display memory
sub ax,ax ;clear to zero-value pixels
mov cx,8000h ;# of words in display memory
rep stosw ;clear all of display memory
pop di ;restore C register vars
pop si
pop bp ;restore caller's stack frame
ret
_Set320x240Mode endp
end

52
tests/pc/vga/L47-2.ASM Normal file
View file

@ -0,0 +1,52 @@
; Mode X (320x240, 256 colors) write pixel routine. Works on all VGAs.
; No clipping is performed.
; C near-callable as:
;
; void WritePixelX(int X, int Y, unsigned int PageBase, int Color);
SC_INDEX equ 03c4h ;Sequence Controller Index
MAP_MASK equ 02h ;index in SC of Map Mask register
SCREEN_SEG equ 0a000h ;segment of display memory in mode X
SCREEN_WIDTH equ 80 ;width of screen in bytes from one scan line
; to the next
parms struc
dw 2 dup (?) ;pushed BP and return address
X dw ? ;X coordinate of pixel to draw
Y dw ? ;Y coordinate of pixel to draw
PageBase dw ? ;base offset in display memory of page in
; which to draw pixel
Color dw ? ;color in which to draw pixel
parms ends
.model small
.code
public _WritePixelX
_WritePixelX proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to local stack frame
mov ax,SCREEN_WIDTH
mul [bp+Y] ;offset of pixel's scan line in page
mov bx,[bp+X]
shr bx,1
shr bx,1 ;X/4 = offset of pixel in scan line
add bx,ax ;offset of pixel in page
add bx,[bp+PageBase] ;offset of pixel in display memory
mov ax,SCREEN_SEG
mov es,ax ;point ES:BX to the pixel's address
mov cl,byte ptr [bp+X]
and cl,011b ;CL = pixel's plane
mov ax,0100h + MAP_MASK ;AL = index in SC of Map Mask reg
shl ah,cl ;set only the bit for the pixel's plane to 1
mov dx,SC_INDEX ;set the Map Mask to enable only the
out dx,ax ; pixel's plane
mov al,byte ptr [bp+Color]
mov es:[bx],al ;draw the pixel in the desired color
pop bp ;restore caller's stack frame
ret
_WritePixelX endp
end

49
tests/pc/vga/L47-3.ASM Normal file
View file

@ -0,0 +1,49 @@
; Mode X (320x240, 256 colors) read pixel routine. Works on all VGAs.
; No clipping is performed.
; C near-callable as:
;
; unsigned int ReadPixelX(int X, int Y, unsigned int PageBase);
GC_INDEX equ 03ceh ;Graphics Controller Index
READ_MAP equ 04h ;index in GC of the Read Map register
SCREEN_SEG equ 0a000h ;segment of display memory in mode X
SCREEN_WIDTH equ 80 ;width of screen in bytes from one scan line
; to the next
parms struc
dw 2 dup (?) ;pushed BP and return address
X dw ? ;X coordinate of pixel to read
Y dw ? ;Y coordinate of pixel to read
PageBase dw ? ;base offset in display memory of page from
; which to read pixel
parms ends
.model small
.code
public _ReadPixelX
_ReadPixelX proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to local stack frame
mov ax,SCREEN_WIDTH
mul [bp+Y] ;offset of pixel's scan line in page
mov bx,[bp+X]
shr bx,1
shr bx,1 ;X/4 = offset of pixel in scan line
add bx,ax ;offset of pixel in page
add bx,[bp+PageBase] ;offset of pixel in display memory
mov ax,SCREEN_SEG
mov es,ax ;point ES:BX to the pixel's address
mov ah,byte ptr [bp+X]
and ah,011b ;AH = pixel's plane
mov al,READ_MAP ;AL = index in GC of the Read Map reg
mov dx,GC_INDEX ;set the Read Map to read the pixel's
out dx,ax ; plane
mov al,es:[bx] ;read the pixel's color
sub ah,ah ;convert it to an unsigned int
pop bp ;restore caller's stack frame
ret
_ReadPixelX endp
end

90
tests/pc/vga/L47-4.ASM Normal file
View file

@ -0,0 +1,90 @@
; Mode X (320x240, 256 colors) rectangle fill routine. Works on all
; VGAs. Uses slow approach that selects the plane explicitly for each
; pixel. Fills up to but not including the column at EndX and the row
; at EndY. No clipping is performed.
; C near-callable as:
;
; void FillRectangleX(int StartX, int StartY, int EndX, int EndY,
; unsigned int PageBase, int Color);
SC_INDEX equ 03c4h ;Sequence Controller Index
MAP_MASK equ 02h ;index in SC of Map Mask register
SCREEN_SEG equ 0a000h ;segment of display memory in mode X
SCREEN_WIDTH equ 80 ;width of screen in bytes from one scan line
; to the next
parms struc
dw 2 dup (?) ;pushed BP and return address
StartX dw ? ;X coordinate of upper left corner of rect
StartY dw ? ;Y coordinate of upper left corner of rect
EndX dw ? ;X coordinate of lower right corner of rect
; (the row at EndX is not filled)
EndY dw ? ;Y coordinate of lower right corner of rect
; (the column at EndY is not filled)
PageBase dw ? ;base offset in display memory of page in
; which to fill rectangle
Color dw ? ;color in which to draw pixel
parms ends
.model small
.code
public _FillRectangleX
_FillRectangleX proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to local stack frame
push si ;preserve caller's register variables
push di
mov ax,SCREEN_WIDTH
mul [bp+StartY] ;offset in page of top rectangle scan line
mov di,[bp+StartX]
shr di,1
shr di,1 ;X/4 = offset of first rectangle pixel in scan
; line
add di,ax ;offset of first rectangle pixel in page
add di,[bp+PageBase] ;offset of first rectangle pixel in
; display memory
mov ax,SCREEN_SEG
mov es,ax ;point ES:DI to the first rectangle pixel's
; address
mov dx,SC_INDEX ;set the Sequence Controller Index to
mov al,MAP_MASK ; point to the Map Mask register
out dx,al
inc dx ;point DX to the SC Data register
mov cl,byte ptr [bp+StartX]
and cl,011b ;CL = first rectangle pixel's plane
mov al,01h
shl al,cl ;set only the bit for the pixel's plane to 1
mov ah,byte ptr [bp+Color] ;color with which to fill
mov bx,[bp+EndY]
sub bx,[bp+StartY] ;BX = height of rectangle
jle FillDone ;skip if 0 or negative height
mov si,[bp+EndX]
sub si,[bp+StartX] ;CX = width of rectangle
jle FillDone ;skip if 0 or negative width
FillRowsLoop:
push ax ;remember the plane mask for the left edge
push di ;remember the start offset of the scan line
mov cx,si ;set count of pixels in this scan line
FillScanLineLoop:
out dx,al ;set the plane for this pixel
mov es:[di],ah ;draw the pixel
shl al,1 ;adjust the plane mask for the next pixel's
and al,01111b ; bit, modulo 4
jnz AddressSet ;advance address if we turned over from
inc di ; plane 3 to plane 0
mov al,00001b ;set plane mask bit for plane 0
AddressSet:
loop FillScanLineLoop
pop di ;retrieve the start offset of the scan line
add di,SCREEN_WIDTH ;point to the start of the next scan
; line of the rectangle
pop ax ;retrieve the plane mask for the left edge
dec bx ;count down scan lines
jnz FillRowsLoop
FillDone:
pop di ;restore caller's register variables
pop si
pop bp ;restore caller's stack frame
ret
_FillRectangleX endp
end

127
tests/pc/vga/L47-5.ASM Normal file
View file

@ -0,0 +1,127 @@
; Mode X (320x240, 256 colors) rectangle fill routine. Works on all
; VGAs. Uses medium-speed approach that selects each plane only once
; per rectangle; this results in a fade-in effect for large
; rectangles. Fills up to but not including the column at EndX and the
; row at EndY. No clipping is performed.
; C near-callable as:
;
; void FillRectangleX(int StartX, int StartY, int EndX, int EndY,
; unsigned int PageBase, int Color);
SC_INDEX equ 03c4h ;Sequence Controller Index
MAP_MASK equ 02h ;index in SC of Map Mask register
SCREEN_SEG equ 0a000h ;segment of display memory in mode X
SCREEN_WIDTH equ 80 ;width of screen in bytes from one scan line
; to the next
parms struc
dw 2 dup (?) ;pushed BP and return address
StartX dw ? ;X coordinate of upper left corner of rect
StartY dw ? ;Y coordinate of upper left corner of rect
EndX dw ? ;X coordinate of lower right corner of rect
; (the row at EndX is not filled)
EndY dw ? ;Y coordinate of lower right corner of rect
; (the column at EndY is not filled)
PageBase dw ? ;base offset in display memory of page in
; which to fill rectangle
Color dw ? ;color in which to draw pixel
parms ends
StartOffset equ -2 ;local storage for start offset of rectangle
Width equ -4 ;local storage for address width of rectangle
Height equ -6 ;local storage for height of rectangle
PlaneInfo equ -8 ;local storage for plane # and plane mask
STACK_FRAME_SIZE equ 8
.model small
.code
public _FillRectangleX
_FillRectangleX proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to local stack frame
sub sp,STACK_FRAME_SIZE ;allocate space for local vars
push si ;preserve caller's register variables
push di
cld
mov ax,SCREEN_WIDTH
mul [bp+StartY] ;offset in page of top rectangle scan line
mov di,[bp+StartX]
shr di,1
shr di,1 ;X/4 = offset of first rectangle pixel in scan
; line
add di,ax ;offset of first rectangle pixel in page
add di,[bp+PageBase] ;offset of first rectangle pixel in
; display memory
mov ax,SCREEN_SEG
mov es,ax ;point ES:DI to the first rectangle pixel's
mov [bp+StartOffset],di ; address
mov dx,SC_INDEX ;set the Sequence Controller Index to
mov al,MAP_MASK ; point to the Map Mask register
out dx,al
mov bx,[bp+EndY]
sub bx,[bp+StartY] ;BX = height of rectangle
jle FillDone ;skip if 0 or negative height
mov [bp+Height],bx
mov dx,[bp+EndX]
mov cx,[bp+StartX]
cmp dx,cx
jle FillDone ;skip if 0 or negative width
dec dx
and cx,not 011b
sub dx,cx
shr dx,1
shr dx,1
inc dx ;# of addresses across rectangle to fill
mov [bp+Width],dx
mov word ptr [bp+PlaneInfo],0001h
;lower byte = plane mask for plane 0,
; upper byte = plane # for plane 0
FillPlanesLoop:
mov ax,word ptr [bp+PlaneInfo]
mov dx,SC_INDEX+1 ;point DX to the SC Data register
out dx,al ;set the plane for this pixel
mov di,[bp+StartOffset] ;point ES:DI to rectangle start
mov dx,[bp+Width]
mov cl,byte ptr [bp+StartX]
and cl,011b ;plane # of first pixel in initial byte
cmp ah,cl ;do we draw this plane in the initial byte?
jae InitAddrSet ;yes
dec dx ;no, so skip the initial byte
jz FillLoopBottom ;skip this plane if no pixels in it
inc di
InitAddrSet:
mov cl,byte ptr [bp+EndX]
dec cl
and cl,011b ;plane # of last pixel in final byte
cmp ah,cl ;do we draw this plane in the final byte?
jbe WidthSet ;yes
dec dx ;no, so skip the final byte
jz FillLoopBottom ;skip this planes if no pixels in it
WidthSet:
mov si,SCREEN_WIDTH
sub si,dx ;distance from end of one scan line to start
; of next
mov bx,[bp+Height] ;# of lines to fill
mov al,byte ptr [bp+Color] ;color with which to fill
FillRowsLoop:
mov cx,dx ;# of bytes across scan line
rep stosb ;fill the scan line in this plane
add di,si ;point to the start of the next scan
; line of the rectangle
dec bx ;count down scan lines
jnz FillRowsLoop
FillLoopBottom:
mov ax,word ptr [bp+PlaneInfo]
shl al,1 ;set the plane bit to the next plane
inc ah ;increment the plane #
mov word ptr [bp+PlaneInfo],ax
cmp ah,4 ;have we done all planes?
jnz FillPlanesLoop ;continue if any more planes
FillDone:
pop di ;restore caller's register variables
pop si
mov sp,bp ;discard storage for local variables
pop bp ;restore caller's stack frame
ret
_FillRectangleX endp
end

113
tests/pc/vga/L47-6.ASM Normal file
View file

@ -0,0 +1,113 @@
; Mode X (320x240, 256 colors) rectangle fill routine. Works on all
; VGAs. Uses fast approach that fans data out to up to four planes at
; once to draw up to four pixels at once. Fills up to but not
; including the column at EndX and the row at EndY. No clipping is
; performed.
; C near-callable as:
; void FillRectangleX(int StartX, int StartY, int EndX, int EndY,
; unsigned int PageBase, int Color);
SC_INDEX equ 03c4h ;Sequence Controller Index
MAP_MASK equ 02h ;index in SC of Map Mask register
SCREEN_SEG equ 0a000h ;segment of display memory in mode X
SCREEN_WIDTH equ 80 ;width of screen in bytes from one scan line
; to the next
parms struc
dw 2 dup (?) ;pushed BP and return address
StartX dw ? ;X coordinate of upper left corner of rect
StartY dw ? ;Y coordinate of upper left corner of rect
EndX dw ? ;X coordinate of lower right corner of rect
; (the row at EndX is not filled)
EndY dw ? ;Y coordinate of lower right corner of rect
; (the column at EndY is not filled)
PageBase dw ? ;base offset in display memory of page in
; which to fill rectangle
Color dw ? ;color in which to draw pixel
parms ends
.model small
.data
; Plane masks for clipping left and right edges of rectangle.
LeftClipPlaneMask db 00fh,00eh,00ch,008h
RightClipPlaneMask db 00fh,001h,003h,007h
.code
public _FillRectangleX
_FillRectangleX proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to local stack frame
push si ;preserve caller's register variables
push di
cld
mov ax,SCREEN_WIDTH
mul [bp+StartY] ;offset in page of top rectangle scan line
mov di,[bp+StartX]
shr di,1 ;X/4 = offset of first rectangle pixel in scan
shr di,1 ; line
add di,ax ;offset of first rectangle pixel in page
add di,[bp+PageBase] ;offset of first rectangle pixel in
; display memory
mov ax,SCREEN_SEG ;point ES:DI to the first rectangle
mov es,ax ; pixel's address
mov dx,SC_INDEX ;set the Sequence Controller Index to
mov al,MAP_MASK ; point to the Map Mask register
out dx,al
inc dx ;point DX to the SC Data register
mov si,[bp+StartX]
and si,0003h ;look up left edge plane mask
mov bh,LeftClipPlaneMask[si] ; to clip & put in BH
mov si,[bp+EndX]
and si,0003h ;look up right edge plane
mov bl,RightClipPlaneMask[si] ; mask to clip & put in BL
mov cx,[bp+EndX] ;calculate # of addresses across rect
mov si,[bp+StartX]
cmp cx,si
jle FillDone ;skip if 0 or negative width
dec cx
and si,not 011b
sub cx,si
shr cx,1
shr cx,1 ;# of addresses across rectangle to fill - 1
jnz MasksSet ;there's more than one byte to draw
and bh,bl ;there's only one byte, so combine the left
; and right edge clip masks
MasksSet:
mov si,[bp+EndY]
sub si,[bp+StartY] ;BX = height of rectangle
jle FillDone ;skip if 0 or negative height
mov ah,byte ptr [bp+Color] ;color with which to fill
mov bp,SCREEN_WIDTH ;stack frame isn't needed any more
sub bp,cx ;distance from end of one scan line to start
dec bp ; of next
FillRowsLoop:
push cx ;remember width in addresses - 1
mov al,bh ;put left-edge clip mask in AL
out dx,al ;set the left-edge plane (clip) mask
mov al,ah ;put color in AL
stosb ;draw the left edge
dec cx ;count off left edge byte
js FillLoopBottom ;that's the only byte
jz DoRightEdge ;there are only two bytes
mov al,00fh ;middle addresses are drawn 4 pixels at a pop
out dx,al ;set the middle pixel mask to no clip
mov al,ah ;put color in AL
rep stosb ;draw the middle addresses four pixels apiece
DoRightEdge:
mov al,bl ;put right-edge clip mask in AL
out dx,al ;set the right-edge plane (clip) mask
mov al,ah ;put color in AL
stosb ;draw the right edge
FillLoopBottom:
add di,bp ;point to the start of the next scan line of
; the rectangle
pop cx ;retrieve width in addresses - 1
dec si ;count down scan lines
jnz FillRowsLoop
FillDone:
pop di ;restore caller's register variables
pop si
pop bp ;restore caller's stack frame
ret
_FillRectangleX endp
end

25
tests/pc/vga/L47-7.C Normal file
View file

@ -0,0 +1,25 @@
/* Program to demonstrate mode X (320x240, 256-colors) rectangle
fill by drawing adjacent 20x20 rectangles in successive colors from
0 on up across and down the screen
*/
#include <conio.h>
#include <dos.h>
void Set320x240Mode(void);
void FillRectangleX(int, int, int, int, unsigned int, int);
void main() {
int i,j;
union REGS regset;
Set320x240Mode();
FillRectangleX(0,0,320,240,0,0); /* clear the screen to black */
for (j = 1; j < 220; j += 21) {
for (i = 1; i < 300; i += 21) {
FillRectangleX(i, j, i+20, j+20, 0, ((j/21*15)+i/21) & 0xFF);
}
}
getch();
regset.x.ax = 0x0003; /* switch back to text mode and done */
int86(0x10, &regset, &regset);
}

View file

@ -16,10 +16,13 @@ LINK=C:\MSDEV\BIN\WIN95\C816\BIN\LINK.EXE
ALL: L23-1.EXE L24-1.EXE L25-1.EXE L25-2.EXE L25-3.EXE L25-4.EXE L26-1.EXE L26-2.EXE L27-1.EXE L27-2.EXE L27-3.EXE \
L28-1.EXE L28-2.EXE L28-3.EXE L29-1.EXE L29-2.EXE L29-3.EXE L29-4.EXE L30-1.EXE L30-2.EXE L31-1.EXE L31-2.EXE \
L33-1.EXE L34-1.EXE L35-2.EXE L35-3.EXE
L33-1.EXE L34-1.EXE L35-1.EXE L35-3.EXE L47-4.EXE
L35-2.EXE: L35-2.OBJ L35-1.OBJ
L35-1.EXE: L35-1.OBJ L35-2.OBJ
$(LINK) $**,$@;
L35-3.EXE: L35-2.OBJ L35-3.OBJ
L35-3.EXE: L35-3.OBJ L35-2.OBJ
$(LINK) $**,$@;
L47-4.EXE: L47-1.OBJ L47-2.OBJ L47-3.OBJ L47-4.OBJ L47-7.OBJ
$(LINK) $**,$@;

View file

@ -25,37 +25,56 @@ Development of PCjs VGA support has just begun (June 2015), so don't expect ever
List of VGA Samples from [Michael Abrash's Graphics Programming Black Book](https://github.com/jeffpar/abrash-black-book):
* [Chapter 23](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-23.md)
* [Chapter 23: Bones and Sinew](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-23.md)
* [L23-1.ASM: Animates four balls bouncing around a playfield by using page flipping and panning](L23-1.ASM)
* [Chapter 24](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-24.md)
* [Chapter 24: Parallel Processing with the VGA](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-24.md)
* [L24-1.ASM: Illustrates operation of ALUs and latches of the VGA's Graphics Controller](L24-1.ASM)
* [Chapter 25](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-25.md)
* [Chapter 25: VGA Data Machinery](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-25.md)
* [L25-1.ASM: Illustrates operation of data rotate and bit mask features of Graphics Controller](L25-1.ASM)
* [L25-2.ASM: Illustrates operation of Map Mask register when drawing to memory that already contains data](L25-2.ASM)
* [L25-3.ASM: Illustrates operation of set/reset circuitry to force setting of memory that already contains data](L25-3.ASM)
* [L25-4.ASM: Illustrates operation of set/reset circuitry in conjunction with CPU data](L25-4.ASM)
* [Chapter 26](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-26.md)
* [Chapter 26: VGA Write Mode 3](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-26.md)
* [L26-1.ASM: Illustrates operation of write mode 3 of the VGA](L26-1.ASM)
* [L26-2.ASM: Illustrates high-speed text-drawing operation of write mode 3 of the VGA](L26-2.ASM)
* [Chapter 27](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-27.md)
* [Chapter 27: Yet Another VGA Write Mode](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-27.md)
* [L27-1.ASM: Illustrates one use of write mode 2 of the VGA and EGA by animating the image of an "A"](L27-1.ASM)
* [L27-2.ASM: Illustrates one use of write mode 2 of the VGA and EGA by drawing lines in color patterns](L27-2.ASM)
* [L27-3.ASM: Illustrates flipping from bit-mapped graphics mode to text mode and back](L27-3.ASM)
* [Chapter 28](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-28.md)
* [Chapter 28: Reading VGA Memory](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-28.md)
* [L28-1.ASM: Illustrates the use of the Read Map register in read mode 0](L28-1.ASM)
* [L28-2.ASM: Illustrates use of read mode 1 (color compare mode) to detect collisions in display memory](L28-2.ASM)
* [L28-3.ASM: Illustrates the use of Color Don't Care to support fast read-modify-write operations](L28-3.ASM)
* [Chapter 29](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-29.md)
* [Chapter 29: Saving Screens and Other VGA Mysteries](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-29.md)
* [L29-1.ASM: Puts up a mode 10h EGA graphics screen, then saves it to the file SNAPSHOT.SCR](L29-1.ASM)
* [L29-2.ASM: Restores a mode 10h EGA graphics screen from the file SNAPSHOT.SCR](L29-2.ASM)
* [L29-3.ASM: Illustrates the color mapping capabilities of the EGA's palette registers](L29-3.ASM)
* [L29-4.ASM: Demonstrates screen blanking via bit 5 of the Attribute Controller Index register](L29-4.ASM)
* [Chapter 30](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-30.md)
* [Chapter 30: Video Est Omnis Divisa](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-30.md)
* [L30-1.ASM: Demonstrates the VGA/EGA split screen in action](L30-1.ASM)
* [L30-2.ASM: Demonstrates the interaction of the split screen and horizontal pel panning](L30-2.ASM)
* [Chapter 31](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-31.md)
* [Chapter 31: Higher 256-Color Resolution on the VGA](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-31.md)
* [L31-1.ASM: Demonstrates pixel drawing in 320x400 256-color mode on the VGA](L31-1.ASM)
* [L31-2.ASM: Demonstrate the two pages available in 320x400 256-color modes on a VGA](L31-2.ASM)
* [L31-2.ASM: Demonstrates the two pages available in 320x400 256-color modes on a VGA](L31-2.ASM)
* [Chapter 32: Be It Resolved: 360x480](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-32.md)
* [L32-1.ASM: Illustrates VGA line drawing in 360x480 256-color mode](L32-1.ASM)
* [L32-2.C: Sample program to illustrate VGA line drawing in 360x480 256-color mode](L32-2.C)
* [Chapter 33: Yogi Bear and Eurythmics Confront VGA Colors](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-33.md)
* [L33-1.ASM: Demonstrates use of the DAC registers by selecting a smoothly contiguous set of 256 colors](L33-1.ASM)
* [Chapter 34: Changing Colors without Writing Pixels](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-34.md)
* [L34-1.ASM: Fills a band across the screen with vertical bars in all 256 attributes](L34-1.ASM)
* [Chapter 35: Bresenham Is Fast, and Fast Is Good](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-35.md)
* [L35-1.C: C implementation of Bresenham's line drawing algorithm](L35-1.C)
* [L35-2.C: Sample program to illustrate EGA/VGA line drawing routines](L35-2.C)
* [L35-3.ASM: Fast assembler implementation of Bresenham's line drawing algorithm](L35-3.ASM)
* [Chapter 47: Mode X: 256-Color VGA Magic](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-35.md)
* [L47-1.ASM: Mode X (320x240, 256 colors) mode set routine](L47-1.ASM)
* [L47-2.ASM: Mode X (320x240, 256 colors) write pixel routine](L47-2.ASM)
* [L47-3.ASM: Mode X (320x240, 256 colors) read pixel routine](L47-3.ASM)
* [L47-4.ASM: Mode X (320x240, 256 colors) rectangle fill routine (slow)](L47-4.ASM)
* [L47-5.ASM: Mode X (320x240, 256 colors) rectangle fill routine (medium)](L47-5.ASM)
* [L47-6.ASM: Mode X (320x240, 256 colors) rectangle fill routine (fast)](L47-6.ASM)
* [L47-7.C: Program to demonstrate mode X (320x240, 256-colors) rectangle fill](L47-7.C)
---