More VGA tests (sprinkled with some Borland/Microsoft compatibility dust)

This commit is contained in:
Jeff Parsons 2015-07-05 13:35:07 -07:00
commit 0313c2d67a
6 changed files with 940 additions and 0 deletions

250
tests/pc/vga/L34-1.ASM Normal file
View file

@ -0,0 +1,250 @@
; Fills a band across the screen with vertical bars in all 256
; attributes, then cycles a portion of the palette until a key is
; pressed.
; Assemble with MASM or TASM
;
USE_BIOS equ 1 ;set to 1 to use BIOS functions to access the
; DAC, 0 to read and write the DAC directly
GUARD_AGAINST_INTS equ 1 ;1 to turn off interrupts and set write index
; before loading each DAC location, 0 to rely
; on the DAC auto-incrementing
WAIT_VSYNC equ 1 ;set to 1 to wait for the leading edge of
; vertical sync before accessing the DAC, 0
; not to wait
NOT_8088 equ 0 ;set to 1 to use REP INSB and REP OUTSB when
; accessing the DAC directly, 0 to use
; IN/STOSB and LODSB/OUT
CYCLE_SIZE equ 256 ;# of DAC locations to cycle, 256 max
SCREEN_SEGMENT equ 0a000h ;mode 13h display memory segment
SCREEN_WIDTH_IN_BYTES equ 320 ;# of bytes across the screen in mode 13h
INPUT_STATUS_1 equ 03dah ;input status 1 register port
DAC_READ_INDEX equ 03c7h ;DAC Read Index register
DAC_WRITE_INDEX equ 03c8h ;DAC Write Index register
DAC_DATA equ 03c9h ;DAC Data register
if NOT_8088
.286
endif ;NOT_8088
.model small
.stack 100h
.data
;Storage for all 256 DAC locations, organized as one three-byte
; (actually three 6-bit values; upper two bits of each byte aren't
; significant) RGB triplet per color.
PaletteTemp db 256*3 dup(?)
.code
start:
mov ax,@data
mov ds,ax
;Select VGA's standard 256-color graphics mode, mode 13h.
mov ax,0013h ;AH = 0: set mode function,
int 10h ; AL = 13h: mode # to set
;Read all 256 DAC locations into PaletteTemp (3 6-bit values, one
; each for red, green, and blue, per DAC location).
if WAIT_VSYNC
;Wait for the leading edge of the vertical sync pulse; this ensures
; that we read the DAC starting during the vertical non-display
; period.
mov dx,INPUT_STATUS_1
WaitNotVSync: ;wait to be out of vertical sync
in al,dx
and al,08h
jnz WaitNotVSync
WaitVSync: ;wait until vertical sync begins
in al,dx
and al,08h
jz WaitVSync
endif ;WAIT_VSYNC
if USE_BIOS
mov ax,1017h ;AH = 10h: set DAC function,
; AL = 17h: read DAC block subfunction
sub bx,bx ;start with DAC location 0
mov cx,256 ;read out all 256 locations
mov dx,seg PaletteTemp
mov es,dx
mov dx,offset PaletteTemp ;point ES:DX to array in which
; the DAC values are to be stored
int 10h ;read the DAC
else ;!USE_BIOS
if GUARD_AGAINST_INTS
mov cx,CYCLE_SIZE ;# of DAC locations to load
mov di,seg PaletteTemp
mov es,di
mov di,offset PaletteTemp ;dump the DAC into this array
sub ah,ah ;start with DAC location 0
DACStoreLoop:
mov dx,DAC_READ_INDEX
mov al,ah
cli
out dx,al ;set the DAC location #
mov dx,DAC_DATA
in al,dx ;get the red component
stosb
in al,dx ;get the green component
stosb
in al,dx ;get the blue component
stosb
sti
inc ah
loop DACStoreLoop
else ;!GUARD_AGAINST_INTS
mov dx,DAC_READ_INDEX
sub al,al
out dx,al ;set the initial DAC location to 0
mov di,seg PaletteTemp
mov es,di
mov di,offset PaletteTemp ;dump the DAC into this array
mov dx,DAC_DATA
if NOT_8088
mov cx,CYCLE_SIZE*3
rep insb ;read CYCLE_SIZE DAC locations at once
else ;!NOT_8088
mov cx,CYCLE_SIZE ;# of DAC locations to load
DACStoreLoop:
in al,dx ;get the red component
stosb
in al,dx ;get the green component
stosb
in al,dx ;get the blue component
stosb
loop DACStoreLoop
endif ;NOT_8088
endif ;GUARD_AGAINST_INTS
endif ;USE_BIOS
;Draw a series of 1-pixel-wide vertical bars across the screen in
; attributes 1 through 255.
mov ax,SCREEN_SEGMENT
mov es,ax
mov di,50*SCREEN_WIDTH_IN_BYTES ;point ES:DI to the start
; of line 50 on the screen
cld
mov dx,100 ;draw 100 lines high
RowLoop:
mov al,1 ;start each line with attr 1
mov cx,SCREEN_WIDTH_IN_BYTES ;do a full line across
ColumnLoop:
stosb ;draw a pixel
add al,1 ;increment the attribute
adc al,0 ;if the attribute just turned
; over to 0, increment it to 1
; because we're not going to
; cycle DAC location 0, so
; attribute 0 won't change
loop ColumnLoop
dec dx
jnz RowLoop
;Cycle the specified range of DAC locations until a key is pressed.
CycleLoop:
;Rotate colors 1-255 one position in the PaletteTemp array;
; location 0 is always left unchanged so that the background
; and border don't change.
push word ptr PaletteTemp+(1*3) ;set aside PaletteTemp
push word ptr PaletteTemp+(1*3)+2 ; setting for attr 1
mov cx,254
mov si,offset PaletteTemp+(2*3)
mov di,offset PaletteTemp+(1*3)
mov ax,ds
mov es,ax
mov cx,254*3/2
rep movsw ;rotate PaletteTemp settings
; for attrs 2 through 255 to
; attrs 1 through 254
pop bx ;get back original settings
pop ax ; for attribute 1 and move
stosw ; them to the PaletteTemp
mov es:[di],bl ; location for attribute 255
if WAIT_VSYNC
;Wait for the leading edge of the vertical sync pulse; this ensures
; that we reload the DAC starting during the vertical non-display
; period.
mov dx,INPUT_STATUS_1
WaitNotVSync2: ;wait to be out of vertical sync
in al,dx
and al,08h
jnz WaitNotVSync2
WaitVSync2: ;wait until vertical sync begins
in al,dx
and al,08h
jz WaitVSync2
endif ;WAIT_VSYNC
if USE_BIOS
;Set the new, rotated palette.
mov ax,1012h ;AH = 10h: set DAC function,
; AL = 12h: set DAC block subfunction
sub bx,bx ;start with DAC location 0
mov cx,CYCLE_SIZE ;# of DAC locations to set
mov dx,seg PaletteTemp
mov es,dx
mov dx,offset PaletteTemp ;point ES:DX to array from which
; to load the DAC
int 10h ;load the DAC
else ;!USE_BIOS
if GUARD_AGAINST_INTS
mov cx,CYCLE_SIZE ;# of DAC locations to load
mov si,offset PaletteTemp ;load the DAC from this array
sub ah,ah ;start with DAC location 0
DACLoadLoop:
mov dx,DAC_WRITE_INDEX
mov al,ah
cli
out dx,al ;set the DAC location #
mov dx,DAC_DATA
lodsb
out dx,al ;set the red component
lodsb
out dx,al ;set the green component
lodsb
out dx,al ;set the blue component
sti
inc ah
loop DACLoadLoop
else ;!GUARD_AGAINST_INTS
mov dx,DAC_WRITE_INDEX
sub al,al
out dx,al ;set the initial DAC location to 0
mov si,offset PaletteTemp ;load the DAC from this array
mov dx,DAC_DATA
if NOT_8088
mov cx,CYCLE_SIZE*3
rep outsb ;load CYCLE_SIZE DAC locations at once
else ;!NOT_8088
mov cx,CYCLE_SIZE ;# of DAC locations to load
DACLoadLoop:
lodsb
out dx,al ;set the red component
lodsb
out dx,al ;set the green component
lodsb
out dx,al ;set the blue component
loop DACLoadLoop
endif ;NOT_8088
endif ;GUARD_AGAINST_INTS
endif ;USE_BIOS
;See if a key has been pressed.
mov ah,0bh ;DOS check standard input status fn
int 21h
and al,al ;is a key pending?
jz CycleLoop ;no, cycle some more
;Clear the keypress.
mov ah,1 ;DOS keyboard input fn
int 21h
;Restore text mode and done.
mov ax,0003h ;AH = 0: set mode function,
int 10h ; AL = 03h: mode # to set
mov ah,4ch ;DOS terminate process fn
int 21h
end start

191
tests/pc/vga/L35-1.C Normal file
View file

@ -0,0 +1,191 @@
/*
* C implementation of Bresenham's line drawing algorithm
* for the EGA and VGA. Works in modes 0xE, 0xF, 0x10, and 0x12.
*
* Compiled with Borland C++
*
* By Michael Abrash
*/
#include <dos.h> /* contains MK_FP macro */
#include "regs.h"
#define EVGA_SCREEN_WIDTH_IN_BYTES 80
/* memory offset from start of
one row to start of next */
#define EVGA_SCREEN_SEGMENT 0xA000
/* display memory segment */
#define GC_INDEX 0x3CE
/* Graphics Controller
Index register port */
#define GC_DATA 0x3CF
/* Graphics Controller
Data register port */
#define SET_RESET_INDEX 0 /* indexes of needed */
#define ENABLE_SET_RESET_INDEX 1 /* Graphics Controller */
#define BIT_MASK_INDEX 8 /* registers */
/*
* Draws a dot at (X0,Y0) in whatever color the EGA/VGA hardware is
* set up for. Leaves the bit mask set to whatever value the
* dot required.
*/
void EVGADot(X0, Y0)
unsigned int X0; /* coordinates at which to draw dot, with */
unsigned int Y0; /* (0,0) at the upper left of the screen */
{
unsigned char far *PixelBytePtr;
unsigned char PixelMask;
/* Calculate the offset in the screen segment of the byte in
which the pixel lies */
PixelBytePtr = MK_FP(EVGA_SCREEN_SEGMENT,
( Y0 * EVGA_SCREEN_WIDTH_IN_BYTES ) + ( X0 / 8 ));
/* Generate a mask with a 1 bit in the pixel's position within the
screen byte */
PixelMask = 0x80 >> ( X0 & 0x07 );
/* Set up the Graphics Controller's Bit Mask register to allow
only the bit corresponding to the pixel being drawn to
be modified */
outportb(GC_INDEX, BIT_MASK_INDEX);
outportb(GC_DATA, PixelMask);
/* Draw the pixel. Because of the operation of the set/reset
feature of the EGA/VGA, the value written doesn't matter.
The screen byte is ORed in order to perform a read to latch the
display memory, then perform a write in order to modify it. */
*PixelBytePtr |= 0xFE;
}
/*
* Draws a line in octant 0 or 3 ( |DeltaX| >= DeltaY ).
*/
void Octant0(X0, Y0, DeltaX, DeltaY, XDirection)
unsigned int X0, Y0; /* coordinates of start of the line */
unsigned int DeltaX, DeltaY; /* length of the line (both > 0) */
int XDirection; /* 1 if line is drawn left to right,
-1 if drawn right to left */
{
int DeltaYx2;
int DeltaYx2MinusDeltaXx2;
int ErrorTerm;
/* Set up initial error term and values used inside drawing loop */
DeltaYx2 = DeltaY * 2;
DeltaYx2MinusDeltaXx2 = DeltaYx2 - (int) ( DeltaX * 2 );
ErrorTerm = DeltaYx2 - (int) DeltaX;
/* Draw the line */
EVGADot(X0, Y0); /* draw the first pixel */
while ( DeltaX-- ) {
/* See if it's time to advance the Y coordinate */
if ( ErrorTerm >= 0 ) {
/* Advance the Y coordinate & adjust the error term
back down */
Y0++;
ErrorTerm += DeltaYx2MinusDeltaXx2;
} else {
/* Add to the error term */
ErrorTerm += DeltaYx2;
}
X0 += XDirection; /* advance the X coordinate */
EVGADot(X0, Y0); /* draw a pixel */
}
}
/*
* Draws a line in octant 1 or 2 ( |DeltaX| < DeltaY ).
*/
void Octant1(X0, Y0, DeltaX, DeltaY, XDirection)
unsigned int X0, Y0; /* coordinates of start of the line */
unsigned int DeltaX, DeltaY; /* length of the line (both > 0) */
int XDirection; /* 1 if line is drawn left to right,
-1 if drawn right to left */
{
int DeltaXx2;
int DeltaXx2MinusDeltaYx2;
int ErrorTerm;
/* Set up initial error term and values used inside drawing loop */
DeltaXx2 = DeltaX * 2;
DeltaXx2MinusDeltaYx2 = DeltaXx2 - (int) ( DeltaY * 2 );
ErrorTerm = DeltaXx2 - (int) DeltaY;
EVGADot(X0, Y0); /* draw the first pixel */
while ( DeltaY-- ) {
/* See if it's time to advance the X coordinate */
if ( ErrorTerm >= 0 ) {
/* Advance the X coordinate & adjust the error term
back down */
X0 += XDirection;
ErrorTerm += DeltaXx2MinusDeltaYx2;
} else {
/* Add to the error term */
ErrorTerm += DeltaXx2;
}
Y0++; /* advance the Y coordinate */
EVGADot(X0, Y0); /* draw a pixel */
}
}
/*
* Draws a line on the EGA or VGA.
*/
void EVGALine(X0, Y0, X1, Y1, Color)
int X0, Y0; /* coordinates of one end of the line */
int X1, Y1; /* coordinates of the other end of the line */
char Color; /* color to draw line in */
{
int DeltaX, DeltaY;
int Temp;
/* Set the drawing color */
/* Put the drawing color in the Set/Reset register */
outportb(GC_INDEX, SET_RESET_INDEX);
outportb(GC_DATA, Color);
/* Cause all planes to be forced to the Set/Reset color */
outportb(GC_INDEX, ENABLE_SET_RESET_INDEX);
outportb(GC_DATA, 0xF);
/* Save half the line-drawing cases by swapping Y0 with Y1
and X0 with X1 if Y0 is greater than Y1. As a result, DeltaY
is always > 0, and only the octant 0-3 cases need to be
handled. */
if ( Y0 > Y1 ) {
Temp = Y0;
Y0 = Y1;
Y1 = Temp;
Temp = X0;
X0 = X1;
X1 = Temp;
}
/* Handle as four separate cases, for the four octants in which
Y1 is greater than Y0 */
DeltaX = X1 - X0; /* calculate the length of the line
in each coordinate */
DeltaY = Y1 - Y0;
if ( DeltaX > 0 ) {
if ( DeltaX > DeltaY ) {
Octant0(X0, Y0, DeltaX, DeltaY, 1);
} else {
Octant1(X0, Y0, DeltaX, DeltaY, 1);
}
} else {
DeltaX = -DeltaX; /* absolute value of DeltaX */
if ( DeltaX > DeltaY ) {
Octant0(X0, Y0, DeltaX, DeltaY, -1);
} else {
Octant1(X0, Y0, DeltaX, DeltaY, -1);
}
}
/* Return the state of the EGA/VGA to normal */
outportb(GC_INDEX, ENABLE_SET_RESET_INDEX);
outportb(GC_DATA, 0);
outportb(GC_INDEX, BIT_MASK_INDEX);
outportb(GC_DATA, 0xFF);
}

81
tests/pc/vga/L35-2.C Normal file
View file

@ -0,0 +1,81 @@
/*
* Sample program to illustrate EGA/VGA line drawing routines.
*
* Compiled with Borland C++
*
* By Michael Abrash
*/
#include <dos.h>
#include "regs.h"
#define GRAPHICS_MODE 0x10
#define TEXT_MODE 0x03
#define BIOS_VIDEO_INT 0x10
#define X_MAX 640 /* working screen width */
#define Y_MAX 348 /* working screen height */
extern void EVGALine();
/*
* Subroutine to draw a rectangle full of vectors, of the specified
* length and color, around the specified rectangle center.
*/
void VectorsUp(XCenter, YCenter, XLength, YLength, Color)
int XCenter, YCenter; /* center of rectangle to fill */
int XLength, YLength; /* distance from center to edge
of rectangle */
int Color; /* color to draw lines in */
{
int WorkingX, WorkingY;
/* Lines from center to top of rectangle */
WorkingX = XCenter - XLength;
WorkingY = YCenter - YLength;
for ( ; WorkingX < ( XCenter + XLength ); WorkingX++ )
EVGALine(XCenter, YCenter, WorkingX, WorkingY, Color);
/* Lines from center to right of rectangle */
WorkingX = XCenter + XLength - 1;
WorkingY = YCenter - YLength;
for ( ; WorkingY < ( YCenter + YLength ); WorkingY++ )
EVGALine(XCenter, YCenter, WorkingX, WorkingY, Color);
/* Lines from center to bottom of rectangle */
WorkingX = XCenter + XLength - 1;
WorkingY = YCenter + YLength - 1;
for ( ; WorkingX >= ( XCenter - XLength ); WorkingX-- )
EVGALine(XCenter, YCenter, WorkingX, WorkingY, Color);
/* Lines from center to left of rectangle */
WorkingX = XCenter - XLength;
WorkingY = YCenter + YLength - 1;
for ( ; WorkingY >= ( YCenter - YLength ); WorkingY-- )
EVGALine(XCenter, YCenter, WorkingX, WorkingY, Color );
}
/*
* Sample program to draw four rectangles full of lines.
*/
void main()
{
char temp;
/* Set graphics mode */
USES_REGS;
_AX = GRAPHICS_MODE;
geninterrupt(BIOS_VIDEO_INT);
/* Draw each of four rectangles full of vectors */
VectorsUp(X_MAX / 4, Y_MAX / 4, X_MAX / 4, Y_MAX / 4, 1);
VectorsUp(X_MAX * 3 / 4, Y_MAX / 4, X_MAX / 4, Y_MAX / 4, 2);
VectorsUp(X_MAX / 4, Y_MAX * 3 / 4, X_MAX / 4, Y_MAX / 4, 3);
VectorsUp(X_MAX * 3 / 4, Y_MAX * 3 / 4, X_MAX / 4, Y_MAX / 4, 4);
/* Wait for the enter key to be pressed */
scanf("%c", &temp);
/* Back to text mode */
_AX = TEXT_MODE;
geninterrupt(BIOS_VIDEO_INT);
}

366
tests/pc/vga/L35-3.ASM Normal file
View file

@ -0,0 +1,366 @@
; Fast assembler implementation of Bresenham's line drawing algorithm
; for the EGA and VGA. Works in modes 0Eh, 0Fh, 10h, and 12h.
; C near-callable.
; Bit mask accumulation technique when |DeltaX| >= |DeltaY|
; suggested by Jim Mackraz.
;
; Assembled with MASM
;
; By Michael Abrash
;
;****************************************************************
; C-compatible line-drawing entry point at _EVGALine. *
; Near C-callable as: *
; EVGALine(X0, Y0, X1, Y1, Color); *
;****************************************************************
;
.model small
.code
;
; Equates.
;
EVGA_SCREEN_WIDTH_IN_BYTES equ 80 ;memory offset from start of
; one row to start of next
; in display memory
EVGA_SCREEN_SEGMENT equ 0a000h ;display memory segment
GC_INDEX equ 3ceh ;Graphics Controller
; Index register port
SET_RESET_INDEX equ 0 ;indexes of needed
ENABLE_SET_RESET_INDEX equ 1 ; Graphics Controller
BIT_MASK_INDEX equ 8 ; registers
;
; Stack frame.
;
EVGALineParms struc
dw ? ;pushed BP
dw ? ;pushed return address (make double
; word for far call)
X0 dw ? ;starting X coordinate of line
Y0 dw ? ;starting Y coordinate of line
X1 dw ? ;ending X coordinate of line
Y1 dw ? ;ending Y coordinate of line
Color db ? ;color of line
db ? ;dummy to pad to word size
EVGALineParms ends
;****************************************************************
; Line drawing macros. *
;****************************************************************
;
; Macro to loop through length of line, drawing each pixel in turn.
; Used for case of |DeltaX| >= |DeltaY|.
; Input:
; MOVE_LEFT: 1 if DeltaX < 0, 0 else
; AL: pixel mask for initial pixel
; BX: |DeltaX|
; DX: address of GC data register, with index register set to
; index of Bit Mask register
; SI: DeltaY
; ES:DI: display memory address of byte containing initial
; pixel
;
LINE1 macro MOVE_LEFT
local LineLoop, MoveXCoord, NextPixel, Line1End
local MoveToNextByte, ResetBitMaskAccumulator
mov cx,bx ;# of pixels in line
jcxz Line1End ;done if there are no more pixels
; (there's always at least the one pixel
; at the start location)
shl si,1 ;DeltaY * 2
mov bp,si ;error term
sub bp,bx ;error term starts at DeltaY * 2 - DeltaX
shl bx,1 ;DeltaX * 2
sub si,bx ;DeltaY * 2 - DeltaX * 2 (used in loop)
add bx,si ;DeltaY * 2 (used in loop)
mov ah,al ;set aside pixel mask for initial pixel
; with AL (the pixel mask accumulator) set
; for the initial pixel
LineLoop:
;
; See if it's time to advance the Y coordinate yet.
;
and bp,bp ;see if error term is negative
js MoveXCoord ;yes, stay at the same Y coordinate
;
; Advance the Y coordinate, first writing all pixels in the current
; byte, then move the pixel mask either left or right, depending
; on MOVE_LEFT.
;
out dx,al ;set up bit mask for pixels in this byte
xchg byte ptr [di],al
;load latches and write pixels, with bit mask
; preserving other latched bits. Because
; set/reset is enabled for all planes, the
; value written actually doesn't matter
add di,EVGA_SCREEN_WIDTH_IN_BYTES ;increment Y coordinate
add bp,si ;adjust error term back down
;
; Move pixel mask one pixel (either right or left, depending
; on MOVE_LEFT), adjusting display memory address when pixel mask wraps.
;
if MOVE_LEFT
rol ah,1 ;move pixel mask 1 pixel to the left
else
ror ah,1 ;move pixel mask 1 pixel to the right
endif
jnc ResetBitMaskAccumulator ;didn't wrap to next byte
jmp short MoveToNextByte ;did wrap to next byte
;
; Move pixel mask one pixel (either right or left, depending
; on MOVE_LEFT), adjusting display memory address and writing pixels
; in this byte when pixel mask wraps.
;
MoveXCoord:
add bp,bx ;increment error term & keep same
if MOVE_LEFT
rol ah,1 ;move pixel mask 1 pixel to the left
else
ror ah,1 ;move pixel mask 1 pixel to the right
endif
jnc NextPixel ;if still in same byte, no need to
; modify display memory yet
out dx,al ;set up bit mask for pixels in this byte.
xchg byte ptr [di],al
;load latches and write pixels, with bit mask
; preserving other latched bits. Because
; set/reset is enabled for all planes, the
; value written actually doesn't matter
MoveToNextByte:
if MOVE_LEFT
dec di ;next pixel is in byte to left
else
inc di ;next pixel is in byte to right
endif
ResetBitMaskAccumulator:
sub al,al ;reset pixel mask accumulator
NextPixel:
or al,ah ;add the next pixel to the pixel mask
; accumulator
loop LineLoop
;
; Write the pixels in the final byte.
;
Line1End:
out dx,al ;set up bit mask for pixels in this byte.
xchg byte ptr [di],al
;load latches and write pixels, with bit mask
; preserving other latched bits. Because
; set/reset is enabled for all planes, the
; value written actually doesn't matter
endm
;
; Macro to loop through length of line, drawing each pixel in turn.
; Used for case of DeltaX < DeltaY.
; Input:
; MOVE_LEFT: 1 if DeltaX < 0, 0 else
; AL: pixel mask for initial pixel
; BX: |DeltaX|
; DX: address of GC data register, with index register set to
; index of Bit Mask register
; SI: DeltaY
; ES:DI: display memory address of byte containing initial
; pixel
;
LINE2 macro MOVE_LEFT
local LineLoop, MoveYCoord, ETermAction, Line2End
mov cx,si ;# of pixels in line
jcxz Line2End ;done if there are no more pixels
shl bx,1 ;DeltaX * 2
mov bp,bx ;error term
sub bp,si ;error term starts at DeltaX * 2 - DeltaY
shl si,1 ;DeltaY * 2
sub bx,si ;DeltaX * 2 - DeltaY * 2 (used in loop)
add si,bx ;DeltaX * 2 (used in loop)
;
; Set up initial bit mask & write initial pixel.
;
out dx,al
xchg byte ptr [di],ah
;load latches and write pixel, with bit mask
; preserving other latched bits. Because
; set/reset is enabled for all planes, the
; value written actually doesn't matter
LineLoop:
;
; See if it's time to advance the X coordinate yet.
;
and bp,bp ;see if error term is negative
jns ETermAction ;no, advance X coordinate
add bp,si ;increment error term & keep same
jmp short MoveYCoord; X coordinate
ETermAction:
;
; Move pixel mask one pixel (either right or left, depending
; on MOVE_LEFT), adjusting display memory address when pixel mask wraps.
;
if MOVE_LEFT
rol al,1
sbb di,0
else
ror al,1
adc di,0
endif
out dx,al ;set new bit mask
add bp,bx ;adjust error term back down
;
; Advance Y coordinate.
;
MoveYCoord:
add di,EVGA_SCREEN_WIDTH_IN_BYTES
;
; Write the next pixel.
;
xchg byte ptr [di],ah
;load latches and write pixel, with bit mask
; preserving other latched bits. Because
; set/reset is enabled for all planes, the
; value written actually doesn't matter
;
loop LineLoop
Line2End:
endm
;****************************************************************
; Line drawing routine. *
;****************************************************************
public _EVGALine
_EVGALine proc near
push bp
mov bp,sp
push si ;preserve register variables
push di
push ds
;
; Point DS to display memory.
;
mov ax,EVGA_SCREEN_SEGMENT
mov ds,ax
;
; Set the Set/Reset and Set/Reset Enable registers for
; the selected color.
;
mov dx,GC_INDEX
mov al,SET_RESET_INDEX
out dx,al
inc dx
mov al,[bp+Color]
out dx,al
dec dx
mov al,ENABLE_SET_RESET_INDEX
out dx,al
inc dx
mov al,0ffh
out dx,al
;
; Get DeltaY.
;
mov si,[bp+Y1] ;line Y start
mov ax,[bp+Y0] ;line Y end, used later in
;calculating the start address
sub si,ax ;calculate DeltaY
jns CalcStartAddress;if positive, we're set
;
; DeltaY is negative -- swap coordinates so we're always working
; with a positive DeltaY.
;
mov ax,[bp+Y1] ;set line start to Y1, for use
; in calculating the start address
mov dx,[bp+X0]
xchg dx,[bp+X1]
mov [bp+X0],dx ;swap X coordinates
neg si ;convert to positive DeltaY
;
; Calculate the starting address in display memory of the line.
; Hardwired for a screen width of 80 bytes.
;
CalcStartAddress:
shl ax,1 ;Y0 * 2 ;Y0 is already in AX
shl ax,1 ;Y0 * 4
shl ax,1 ;Y0 * 8
shl ax,1 ;Y0 * 16
mov di,ax
shl ax,1 ;Y0 * 32
shl ax,1 ;Y0 * 64
add di,ax ;Y0 * 80
mov dx,[bp+X0]
mov cl,dl ;set aside lower 3 bits of column for
and cl,7 ; pixel masking
shr dx,1
shr dx,1
shr dx,1 ;get byte address of column (X0/8)
add di,dx ;offset of line start in display segment
;
; Set up GC Index register to point to the Bit Mask register.
;
mov dx,GC_INDEX
mov al,BIT_MASK_INDEX
out dx,al
inc dx ;leave DX pointing to the GC Data register
;
; Set up pixel mask (in-byte pixel address).
;
mov al,80h
shr al,cl
;
; Calculate DeltaX.
;
mov bx,[bp+X1]
sub bx,[bp+X0]
;
; Handle correct one of four octants.
;
js NegDeltaX
cmp bx,si
jb Octant1
;
; DeltaX >= DeltaY >= 0.
;
LINE1 0
jmp EVGALineDone
;
; DeltaY > DeltaX >= 0.
;
Octant1:
LINE2 0
jmp short EVGALineDone
;
NegDeltaX:
neg bx ;|DeltaX|
cmp bx,si
jb Octant2
;
; |DeltaX| >= DeltaY and DeltaX < 0.
;
LINE1 1
jmp short EVGALineDone
;
; |DeltaX| < DeltaY and DeltaX < 0.
;
Octant2:
LINE2 1
;
EVGALineDone:
;
; Restore EVGA state.
;
mov al,0ffh
out dx,al ;set Bit Mask register to 0ffh
dec dx
mov al,ENABLE_SET_RESET_INDEX
out dx,al
inc dx
sub al,al
out dx,al ;set Enable Set/Reset register to 0
;
pop ds
pop di
pop si
pop bp
ret
_EVGALine endp
end

25
tests/pc/vga/MAKEFILE Normal file
View file

@ -0,0 +1,25 @@
CL=C:\MSDEV\BIN\WIN95\C816\BIN\CL.EXE
INCLUDE=C:\MSDEV\BIN\WIN95\C816\INC
LIB=C:\MSDEV\BIN\WIN95\C816\LIB
MASM=C:\MSDEV\BIN\WIN95\MASM\MASM.EXE
LINK=C:\MSDEV\BIN\WIN95\C816\BIN\LINK.EXE
.C.OBJ:
$(CL) /c $<
.ASM.OBJ:
$(MASM) $<;
.ASM.EXE:
$(MASM) $*;
$(LINK) $*;
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
L35-2.EXE: L35-2.OBJ L35-1.OBJ
$(LINK) $**,$@;
L35-3.EXE: L35-2.OBJ L35-3.OBJ
$(LINK) $**,$@;

27
tests/pc/vga/REGS.H Normal file
View file

@ -0,0 +1,27 @@
#ifdef __TURBOC__
#define USES_REGS
#else
#define USES_REGS union REGS reg; struct SREGS sreg;
#define _AH reg.h.ah
#define _AL reg.h.al
#define _BH reg.h.bh
#define _BL reg.h.bl
#define _CH reg.h.ch
#define _CL reg.h.cl
#define _DH reg.h.dh
#define _DL reg.h.dl
#define _AX reg.x.ax
#define _BX reg.x.bx
#define _CX reg.x.cx
#define _DX reg.x.dx
#define _SI reg.x.si
#define _DI reg.x.di
#define _DS sreg.ds
#define _ES sreg.es
#define _SS sreg.ss
#define geninterrupt(n) int86x(n,&reg,&reg,&sreg)
#define inport(port) _inpw(port)
#define inportb(port) _inp(port)
#define outport(port,data) _outpw(port,data)
#define outportb(port,data) _outp(port,data)
#endif