diff --git a/tests/pc/vga/L34-1.ASM b/tests/pc/vga/L34-1.ASM new file mode 100644 index 000000000..204a09477 --- /dev/null +++ b/tests/pc/vga/L34-1.ASM @@ -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 + diff --git a/tests/pc/vga/L35-1.C b/tests/pc/vga/L35-1.C new file mode 100644 index 000000000..43b21c38d --- /dev/null +++ b/tests/pc/vga/L35-1.C @@ -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 /* 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); +} diff --git a/tests/pc/vga/L35-2.C b/tests/pc/vga/L35-2.C new file mode 100644 index 000000000..202dc4083 --- /dev/null +++ b/tests/pc/vga/L35-2.C @@ -0,0 +1,81 @@ +/* + * Sample program to illustrate EGA/VGA line drawing routines. + * + * Compiled with Borland C++ + * + * By Michael Abrash + */ + +#include +#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); +} diff --git a/tests/pc/vga/L35-3.ASM b/tests/pc/vga/L35-3.ASM new file mode 100644 index 000000000..b886b8603 --- /dev/null +++ b/tests/pc/vga/L35-3.ASM @@ -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 diff --git a/tests/pc/vga/MAKEFILE b/tests/pc/vga/MAKEFILE new file mode 100644 index 000000000..6c3c356ea --- /dev/null +++ b/tests/pc/vga/MAKEFILE @@ -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) $**,$@; diff --git a/tests/pc/vga/REGS.H b/tests/pc/vga/REGS.H new file mode 100644 index 000000000..77e9f0b20 --- /dev/null +++ b/tests/pc/vga/REGS.H @@ -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,®,®,&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