/* The following software can be used and modified freely. I can not however be sold in any way. This includes both seperately and compiled into programs. The following is the work I have done on creating .BGI driver files for TURBO-C 2.0 and TURBO PASCAL. Although the majority of the functions work and can be used as is, it is by no means a complete . My main reason for posting this is so that people who want to create their own BGI drivers have a good starting point and a good example to follow. The routines that to block fills/reads have been written a little trickier than need be. This was to make them fast enough to be usable (the tricks are somewhat described in the code where they are used). The two main ingredients that went into this were: 1. A skeleton of a BGI header which was posted to the net sometime ago. 2. The book "Graphics Programming in Turbo-C 2.0" written by Ben Ezzell and published by Addison-Wesley. I found it a B-Dalton. This book is almost a must if you hope to write a BGI driver. I will help answer questions (at least until I get tired of it). Mainly I will look gather up all the commo questions and post them to the net. I also want to make it clear that I am basically a High level language programmer and had to learn Assembly as I went to write this. Therefor, I DONT want to here any comments about my poor assembly language programming style. REMEMBER, THIS IS MAINLY INTENDED AS A DEVELOPEMENT EXAMPLE AND IS NOT INTENDED TO BE A RELEASED PRODUCT ( although almost all of the functions do work for this graphics card ). ======================= CUT APART HERE AND NAME BGITEST.C =========== /* This is a real hack of a demo program but it at least shows you what your C program must do to install and use a user-written device driver. I had most of the BORLAND SUPPLIED bgidemo.c program working after I took out their palette demo routine and change the program to use another font other than DEFAULT ( which I could not make work ). I didn't post I because I was afraid BORLAND could bust me for posting it. */ #include #include #include int huge detectLEEVGA(void); main() { int driver, mode; int ysize, xsize; int i; driver =installuserdriver ("LEEVGA", detectLEEVGA); { int ec; if ((ec=graphresult()) < 0) { printf("unable to register\n"); printf("reason <%s>\n", grapherrormsg(ec)); exit(-1); return(-1); } } driver = DETECT; initgraph(&driver, &mode, ""); { int ec; if ((ec=graphresult()) < 0) { printf("unable to open this graphics mode\n"); printf("reason <%s>\n", grapherrormsg(ec)); exit(-1); return(-1); } } printf(" max x= %d\n", xsize=getmaxx()); printf(" max y= %d\n", ysize=getmaxy()); printf(" modename= %s\n", getmodename(0)); printf(" drivername= %s\n", getdrivername()); printf(" maxcolors= %d\n", getmaxcolor()); printf(" palette size=%d\n", getpalettesize()); #define DO 1 #ifdef DO setfillstyle ( SOLID_FILL, 15 ); setcolor ( 12 ); outtextxy(xsize/6,ysize/3,"A b C d E f G h I J k"); getch(); printf(" Outtext ( DEFAULT FONT ) is done\n"); getch(); settextstyle(TRIPLEX_FONT,HORIZ_DIR,4); outtextxy(xsize/6,ysize/4,"0 1 2 3 4 5 6 7 8 9 0"); printf(" Outtext ( TRIPLEX_FONT ) is done\n"); getch(); setlinestyle(DASHED_LINE,0,NORM_WIDTH); for (i=10; i < xsize/3*2 ; i++) { putpixel(i,ysize/4,12); } printf(" pixel puts are done, press a key\n");getch(); #endif setfillstyle ( SLASH_FILL, 11 ); setlinestyle(DASHED_LINE,0,NORM_WIDTH); pieslice(xsize/2,ysize/2,45,45+90,ysize/3); circle (xsize/2, ysize/2,ysize/2); ellipse(xsize/2,ysize/2,0,0,100,100); printf(" circle done press a key\n");getch(); setlinestyle(CENTER_LINE,0,NORM_WIDTH); line ( 10,ysize/2,ysize-100,ysize/2); line ( xsize/2, 10 , xsize/2 , ysize/2 ); printf(" lines done, press a key\n");getch(); rectangle (10,20,xsize/6,ysize/2); printf(" rectangle done, press a key\n");getch(); setlinestyle(DOTTED_LINE,0,THICK_WIDTH); setfillstyle ( XHATCH_FILL, 21 ); setcolor(83); bar3d ( xsize/2,ysize/2, xsize/6*5,ysize, 50, 1); printf(" bar is done, press a key\n");getch(); setlinestyle(DASHED_LINE,0,NORM_WIDTH); { int pp[10]={100,100, 100,150, 180,195, 300,150, 330,100}; setfillstyle ( CLOSE_DOT_FILL, 11 ); fillpoly ( 5 , &pp ); printf(" filled polygon is done, press a key\n");getch(); } setrgbpalette(12,1,2,3); { typedef struct { char r,g,b; } entry; struct p { char size; entry index[256]; } palette; palette.size = 3*256; palette.index[0].r =1; palette.index[0].g =2; palette.index[0].b =3; setallpalette(&palette); } cleardevice(); printf(" clear done\n"); printf(" press a key\n");getch(); closegraph(); } /* ______________________ */ int huge detectLEEVGA(void) { printf("detecting\n"); /* You must write code here to detect whether or not the graphics card is available and what its maximum mode is. By returning the value of ONE here, I am telling it I have detected my graphics card and the mode I want it to use is ONE. */ return(1); } ======================== CUT APART HERE and NAME IT LEEVGA.ASM ========== ; know problems ; 1. the default font has not been done. ; 2. flood fill has not been done. ; to compile (create) the .BGI file use the following ; masm leevga.asm leevga.obj NUL.LST NUL.LST ; link leevga.obj,leevga.exe,NUL.MAP,.LIB ; exe2bin leevga.exe leevga.bgi ; del leevga.exe ; del leevga.obj ;------------------------------------------------------; ; LEEBGI.BGI ; ; Borland Graphic Interface for TP 5.0, TC2.0 ; ; Paridise VGA Graphics Board ; ; Copyright (c) 1989 Gene W. Lee ; ;------------------------------------------------------; ; ACODE segment 'BGI' ; ; astart DB 'pk' ; BGI magic word (Phillipe K.) DB 8,8 DB 'BGI Device Driver V1.0 Gene W. Lee - 1989 ' db 0Dh,0Ah DB 'Copyright (c) 1989 Gene W. Lee',0Dh,0Ah db 0,1Ah dw 00A0h,11 dw endofcode ; This is a label that precedes CODE ENDS dw 2 db 1,1 org 80h dw 00A0h,11 dw endofcode ; see above dw 2 db 1,1 ; The following is returned by GetDriverName. db 6,'LEEVGA' alen = $-astart ; Pad this segment out to db (160 - alen) dup(0) ; 160 bytes ACODE ends CODE SEGMENT 'BGI' ; ; Now for the real BGI driver code. A separate segment is required ; because although this code starts at offset 0A0h in the file ; ATTDEB.BGI, GRAPH will adjust its address up by 10 paras and then ; do a CALL FAR xxxx:[0000]. So all memrefs have to be 0 relative to ; the label BGIENTRY below. IMPORTANT: All code from here to JMP_TABLE ; needs to be exactly as it is. GRAPH expects the label EMULATE to be ; at the offset that it is in this code (GRAPH is going to shove code ; ASSUME CS:CODE,DS:CODE,ES:CODE BGIENTRY PROC FAR ; "FAR" So that the RET is a RETF PUSH DS PUSH CS POP DS CLD PUSH BP ; ; The Function number to be performed in SI. SI is used to index into ; a jump table. Note: all SI values are even. ; CALL [SI+JMP_TABLE] ; See JMP_TABLE POP BP POP DS RET BGIENTRY ENDP ; db 'CB' ; ? dw 0 ; EMULATE PROC NEAR ; This is the entry point of functions RET ; you want Turbo XX to emulate. Note: dw 0 ; only select entries may be emulated. dw 0 EMULATE ENDP NOP_02 PROC NEAR ; This is the entry point for all functions RET ; which are not implemented. NOP_02 ENDP ; Now for the Jump Table; The SVC_xx labels correspond to the ; value in SI when the driver is called(ie: SVC_00 is the entry point ; when SI = 0. IF the labels are EMULATE or NOP_02 the value of SI is ; to the right in braces. NOP_02 means this function is a NOP and ; GRAPH should never call it (at least relative to TP5). The label ; EMULATE is a JMP to a CALL back to GRAPH to perform the function. ; (Aren't you glad you don't have to write a Circle Drawing Algorithm!) ; ; It should also be noted that calls to the BGI code are both pre- ; processed and post-processed by GRAPH. Some calls never make it to ; your code. One example is the DisplayText functions. The only time ; that calls make it here is for the bit-map text displays. GRAPH ; handles all of the stroked CHR fonts. ; jmp_table dw install ; {0} Initialize dw init ; {2} Set Mode dw clear ; {4} Clear Graphics Screen dw nop_02 ; {06} POST dw move ; {8} current pointer (CP) dw draw ; {A} draw line from CP to new CP dw vect ; {C} Draw Line dw EMULATE ; {0E} Draw/Fill Poly dw emulate ; {10} Bar3D dw patbar ; {12} pattern Bar dw EMULATE ; {14} Draw Circle dw EMULATE ; {16} Draw Pie Slice dw EMULATE ; {18} Draw Ellipse dw palette ; {1A} Set Palette dw SVC_1C ; {1C} Set All Palette dw color ; {1E} Set Color dw fillstyle ; {20} Set Fill Style/Pattern dw linestyle ; {22} Set Line Style/Pattern dw textstyle ; {24} Set UserCharSize dw text ; {26} Display Text dw textsize ; {28} Text Width/Height dw EMULATE ; {2A} ; I never figured this one out ! dw flood_fill ; {2C} FloodFill dw get_pixel ; {2E} GetPixel dw put_pixel ; {30} SetPixel dw bitmaputil ; {32} Set CallTable dw get_image ; {34} GetImage dw put_image ; {36} PutImage dw set_clip ; {38} Set View Min/Max dw color_query ; {3A} SetParameters dw emulate ; {3C} reserved dw EMULATE ; {3E} symbol dw NOP_03 ; 40 dw NOP_03 ; 42 dw NOP_03 ; 44 dw NOP_03 ; 46 dw NOP_03 ; 48 dw NOP_03 ; 4A dw NOP_03 ; 4C dw NOP_03 ; 4E dw NOP_03 ; 50 dw NOP_03 ; 52 dw NOP_03 ; 54 dw NOP_03 ; 56 dw NOP_03 ; 46 dw NOP_03 ; 48 dw NOP_03 ; 4A dw NOP_03 ; 4C dw NOP_03 ; 4E dw NOP_03 ; 50 dw NOP_03 ; 52 dw NOP_03 ; 54 dw NOP_03 ; 56 dw NOP_03 ; 46 dw NOP_03 ; 48 dw NOP_03 ; 4A dw NOP_03 ; 4C dw NOP_03 ; 4E dw NOP_03 ; 50 dw NOP_03 ; 46 dw NOP_03 ; 48 dw NOP_03 ; 52 dw NOP_03 ; 54 dw NOP_03 ; 56 ; NOP_03 PROC FAR ; stub procedure for far returns RET NOP_03 ENDP ; ; structure for 320x200 256 color mode statustype0 struc db 0 ; current device status db 0 ; device type identifier ( must be zero ) dw 319 ; full resolution in the x direction dw 199 ; full resolution in the y direction xefres0 dw 319 ; effective resolution in x direction dw 199 ; effective resolution in y direction dw 9000 ; device x size in inches * 1000 dw 7000 ; device y size in inches * 1000 dw 8572 ; aspect ratio = ( ysize / xsize ) * 1000 ; next four bytes are for compatibilty db 8h db 8h db 90h db 90h statustype0 ends status0 statustype0<> ; structure for 640x400 256 color mode (0x5E) statustype1 struc db 0 ; current device status db 0 ; device type identifier ( must be zero ) dw 639 ; full resolution in the x direction dw 399 ; full resolution in the y direction xefres1 dw 639 ; effective resolution in x direction dw 399 ; effective resolution in y direction dw 9000 ; device x size in inches * 1000 dw 7000 ; device y size in inches * 1000 dw 8572 ; aspect ratio = ( ysize / xsize ) * 1000 ; next four bytes are for compatibilty db 8h db 8h db 90h db 90h statustype1 ends status1 statustype1<> active_mode db ? ; the currently active mode number modestring0 db 18,'320 x 200 VGA Mode',0 modestring1 db 18,'640 x 400 VGA Mode',0 xsize dw ? ; the xsize of the currently selected mode max_x dw ? ; the maximum value of x for the current mode ; table for ellipse function calls ; Let it used my put_pixel routine, there is not much I could do to speed ; up pixel writing anyway. the_util_table dw offset NOP_03 ; enter pixel mode dw offset NOP_03 ; Leave pixel mode dw offset NOP_03 ; write a pixel dw offset NOP_03 ; get a pixel dw offset bitsperpixel ; return bits per pixel in AX dw offset NOP_03 ; set draw page dw offset NOP_03 ; set visual page dw offset NOP_03 ; set xor mode enter_pixel_mode proc far call unlock_vga mov doing_ellipse,1 ret enter_pixel_mode endp exit_pixel_mode proc far call lock_vga mov doing_ellipse,0 ret exit_pixel_mode endp bitsperpixel proc far mov ax,8 ; eight bits per pixel in VGA mode ret bitsperpixel endp ; the bit patterns for default linestyles default_linestyles dw 0FFFFh ; SOLID_LINE dw 0CCCCh ; DOTTED_LINE dw 0FC78h ; CENTER_LINE dw 0F8F8h ; DASHED_LINE active_linestyle dw 0FFFFh ; the currently selected line style is written ; into this word. line_bit_ptr db 0 ; pointer into linestyle word (bit ptr) default_fillpaterns db 000h,000h,000h,000h,000h,000h,000h,000h ; No Fill db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh ; Solid fill db 0FFh,0FFh,000h,000h,0FFh,0FFh,000h,000h ; Line FIll db 001h,002h,004h,008h,010h,020h,040h,080h ; Lt Slash fil db 0E0h,0C1h,083h,007h,00Eh,01Ch,038h,070h ; Slash fill db 0F0h,078h,03Ch,01Eh,00Fh,087h,0C3h,0E1h ; Backslash db 0A5h,0D2h,069h,0B4h,05Ah,02Dh,096h,04Bh ; lt Backslash db 0FFh,088h,088h,088h,0FFh,088h,088h,088h ; Hatch Fill db 081h,042h,024h,018h,018h,024h,042h,081h ; XHatch Fill db 0CCh,033h,0CCh,033h,0CCh,033h,0CCh,033h ; interleave db 080h,000h,008h,000h,080h,000h,008h,000h ; Wide Dot db 088h,000h,022h,000h,088h,000h,022h,000h ; Close Dot active_fillpattern db ?,?,?,?,?,?,?,? ; the currently selected fill ; style is written into this array. fill_byte_ptr dw ? ; pointer to fill pattern byte fill_bit_ptr dw ? ; pointer to bit of fill pattern byte ; I don't really understand what this is all about. I just tell it I don't ; have a default color table color_table db 0 current_background_color db ? current_line_color db ? ; the currently selected color for lines current_fill_color db ? ; the currently selected fill color cp_x dw ? ; the current pointer - x cp_y dw ? ; the current pointer - y clip_x1 dw ? ; clipping window clip_y1 dw ? ; clipping window clip_x2 dw ? ; clipping window clip_y2 dw ? ; clipping window ; start x,y and end x,y for line draws x1 dw ? y1 dw ? x2 dw ? y2 dw ? ; vars used by line draw routine delta_x dw ? delta_y dw ? count dw ? halfx label word ; only need halfx or halfy at one time halfy dw ? ; vars used to do pixel read/writes in VGA mode lasthio dw -1 ; keeps track of the last hioffset value calculated next_row dw ? ; variable used by patbar, getimage and putimage ; vars used by palette routines red db ? green db ? blue db ? doing_ellipse db 0 ; set true when unlock has been called to start ; drawing an ellipse. Saves time on ellipse draws ; 0 ---------------------------------------------------------------------- ; if al=0 ( install device ) ; input: cl = mode number for device ; ch = autodetect maximum device number ; return: es:bx = pointer to device status table ; if al=1 ( mode query ) ; input: nothing ; return: cx = number of modes supported by this device ; if al=2 ( return mode name ) ; input cx: the mode number for query ; return es:bx = pointer to PASCAL string containing the name install: cmp al,0 jz installit cmp al,1 jz modequery ; else it must be a modename() call push cs pop es cmp cx,0 je modename0 mov bx, offset modestring1 ; return pointer to mode name ret modename0: mov bx, offset modestring0 ; return pointer to mode name ret installit: mov active_mode,cl ; remember what mode were in cmp cl,1 je install_mode1 ; else we install mode 0 ; keep track of current x and y screen size mov ax,status0.xefres0 mov max_x,ax inc ax mov xsize,ax push cs pop es mov bx,offset status0 ; return location of status table ret install_mode1: mov ax,status1.xefres1 ; keep track of current x and y screen size mov max_x,ax inc ax mov xsize,ax push cs pop es mov bx,offset status1 ; return location of status table ret modequery: mov cx,2 ; only two modes supported for now ret ; 2 ---------------------------------------------------------------------- ; input: ; es:bx = pointer to device table ; return: nothing init: cmp active_mode,1 je init_mode1 ; else we init mode 0 mov ax,13h int 10h ret init_mode1: mov ax,5Eh int 10h ret ; 4 ---------------------------------------------------------------------- ; input: nothing ; output: nothing ; clear the device clear: call init ret ; 8 ------------------------------------------------------------------------ ; input: ; ax = the new CP x coord ; bx = the new CP y coord ; return: nothing move: ; set current pointer (CP) ; load the new current pointer mov cp_x,ax mov cp_y,bx ret ; A ----------------------------------------------------------------------- ; draw line from CP to x,y ; input: ; ax: ending x coord ; bx: ending y coord ; output: ; nothing draw: ; draw line from CP to new CP push cx mov cx,cp_x ; draw starts at current position (cp) mov x1,cx mov cx,cp_y mov y1,cx pop cx mov x2,ax ; load the end points mov y2,bx mov cp_x,ax ; end points are now new current points mov cp_y,bx mov line_bit_ptr,0 ; reset the line style call draw_line ; draw the line from x1,y1 to x2,y2 ret ; C ---------------------------------------------------------------------- ; draw line from x1,y1 to x2,y2 ; input: ; ax = x1 ; bx = y1 ; cx = x2 ; dx = y1 ; output: ; nothing vect: ; Draw Line ; setup the start and end points mov x1,ax mov y1,bx mov x2,cx mov y2,dx mov line_bit_ptr,0 ; reset the line style call draw_line ; draw a line ret ; 1A -------------------------------------------------------------------- ; set a single palette entry ; input: ; ax = the index number to load ; bx = the color value ; return: nothing palette: push ax push cx mov cl,14 shr ax,cl ; only want upper to bits of cl cmp ax,010b ; set rgb palette pop cx pop ax jne p_done ; the other values are meaningless in vga mode mov red,bl mov green,cl mov blue,dl ; get rid of upper two control bits, leave index and ax,03FFFh mov dh,red mov ch,green mov cl,blue mov bx,ax mov ax,01010h ; set single palette entry int 10h p_done: ret ; 1C --------------------------------------------------------------------- ; Set all Palette entries with one call SVC_1C: ; Set All Palette ; not compatible with 256 color VGA modes, you have to provide ; the user with a library function to do this. ret ; 1E ------------------------------------------------------------------- ; input: ; al = index number of current drawing color ; ah = index number of current fill color ; return: nothing color: ; Set Color mov current_line_color,al mov current_fill_color,ah ret ; 20 ------------------------------------------------------------------ ; input: ; al = fill pattern number ; es:bx = if pattern number = 0FFh, this points to user fill pattern ; return: nothing fillstyle: ; Set Fill Style/Pattern cmp al,0FFh ; test if user defined fill jz user_fill cbw mov cl,3 ; multiply by 8 for eight bytes per fill type shl ax,cl mov si,ax add si,offset default_fillpaterns ; point to the selected fill patern jmp cont_fill ; ds:si point to selected fill pattern user_fill: ; user defined fill passed by pointer ES:BX push bx pop si push es pop ds ; setup so ds:si points to users fill pattern cont_fill: mov di,offset active_fillpattern push cs pop es ; es:cs is the destination of the copy mov cx,8 ; setup to copy eight bytes of pattern repz movsb ; copy pattern into active pattern ret ; 22 ------------------------------------------------------------------ ; input: ; al = line pattern number ; bx = user defined line pattern if al = 4 ; cx = line width ; return: nothing linestyle: ; set current line style cmp ax,4 jz user_defined ; check if its a user defined line and ax,0FFh ; pattern number in AL shl al,1 ; used two index by 2 bytes at a time mov si,ax mov bx,[default_linestyles +si] ; load a pre-defined pattern user_defined: ; user defined are passed in BX mov active_linestyle,bx ret ; 24 ------------------------------------------------------------------- ; input: ; al = hardware font number ; ah = hardware font orientation ; bx = desired X size ; cx = desired Y size ; return: ; bx = closest X size available ; cx = closest Y size available textstyle: ; Set UserCharSize ; this code works but thats about all mov bx,bx ; let say the characters are 8 x 16 mov cx,cx ret ; 26 -------------------------------------------------------------------- ; input: ; es:bx = pointer to ascii string to display ; cx = number of chars in string ; al = horizontal justification ( 0=left,1=center,2=right ) ; ah = vertical justification (0=bottom,1=center,2=top) ; return: ; bx = width of string in graphics units ; cx = height of string in graphics units text: ; Display Text ( DEFAULT FONT ) ; this is one of the things I could not figure out ; if you come up with something please let me know. push bx push cx ; hack here ; end hack pop cx pop bx mov bx,bx ; NOTE this should be 8 * length of string (cx) mov cx,cx ret ; 28 -------------------------------------------------------------------- ; input: ; es:bx = pointer to ascii text ; cx = number of chars in test ; return: ; bx = width of string in graphics units ; cx = height of string in graphics units textsize: ; Text Width/Height ( DEFAULT FONT ) ; again I did not know how to do this mov bx,bx ; NOTE this is not right mov cx,cx ret ; 2C -------------------------------------------------------------------- ; input: ; ax = x coord to start at ; bx = y coord to start at ; cl = boundry color ; return: nothing flood_fill: ; FloodFill ; flood fill needs implemented also ret ; 2E ----------------------------------------------------------------- ; input: ; ax = x coord ; bx = y coord ; return: ; dl = color get_pixel: ; GetPixel ; if you don't mind being slow, just use the bios calls ; mov cx,ax ; load x value ; mov dx,bx ; load y value ; mov bh,0 ; page zero ; mov ah,0Dh ; read pixel function ; ; int 10h ; ; mov dl,al ; return the color of pixel read ; ret cmp active_mode,1 je getpixel_mode1 ; else read pixels in mode 0 push di ; save these registers push es mov cx,ax ; save the column value (x) mov ax,bx ; load the row value (y) mul xsize ; offset =(xsize * y) add ax,cx ; + x mov di,ax ; load screen address into di mov ax,0A000h ; video ram segement address mov es,ax mov dl,es:[di] ; read the pixel pop es pop di ret getpixel_mode1: ; read pixel this way when in mode 1 push di ; save these registers push es mov cx,ax ; save the column value (x) mov ax,bx ; load the row value (y) call calc_hilow ; find vga offset values into screen memory cmp bx,lasthio ; see of hioffset is the same as last time je no_hio_chg_r ; if it is, save time by not resetting it cmp doing_ellipse,1 je no_unlock_get ; its already unlocked call unlock_vga no_unlock_get: mov ax,bx ; call set_hioffset ; sets the hioffset register for vga mem addressing cmp doing_ellipse,1 je no_lock_get call lock_vga no_lock_get: no_hio_chg_r: mov ax,0A000h ; video ram segement address mov es,ax mov dl,es:[di] ; read the pixel pop es pop di ret ; 30 ----------------------------------------------------------------- ; input: ; ax = x coord ; bx = y coord ; dl = color ; return: nothing put_pixel: ; SetPixel ; p[XSIZE*y+x] = color; for 320 x 200 - 256 mode ; if you don't mind being slow, just call the bios plot dot ; push dx ; save color value ; mov cx,ax ; load x value ; mov dx,bx ; load y value ; pop ax ; color value from pushed dx ; mov bh,0 ; page zero ; mov ah,0Ch ; write pixel function ; ; int 10h ; ret ; else get fancy to make it faster. cmp active_mode,1 je putpixel_mode1 ; else plot pixels in mode 0 push di ; save these registers push es push ax push bx push cx push dx push dx ; save the color mov cx,ax ; save the column value (x) mov ax,bx ; load the row value (y) mul xsize ; offset =(xsize * y) add ax,cx ; + x mov di,ax ; load screen address into di mov ax,0A000h ; video ram segement address mov es,ax pop dx ; get the color info back mov es:[di],dl ; plot the pixel pop dx pop cx pop bx pop ax pop es pop di ret putpixel_mode1: ; routine to plot pixels when in mode 1 push di ; save these registers push es push ax push bx push cx push dx push dx ; save the color mov cx,ax ; save the column value (x) mov ax,bx ; load the row value (y) call calc_hilow ; find vga hi and low offset value for screen mem cmp bx,lasthio ; see of hioffset is the same as last time je no_hio_chg_w ; if it is, save time by not resetting it cmp doing_ellipse,1 je no_unlock_put ; its already done call unlock_vga no_unlock_put: mov ax,bx ; call set_hioffset ; sets the hioffset register for vga mem addressing cmp doing_ellipse,1 je no_lock_put call lock_vga no_lock_put: no_hio_chg_w: mov ax,0A000h ; video ram segement address mov es,ax pop dx ; get the color info back mov es:[di],dl ; plot the pixel pop dx pop cx pop bx pop ax pop es pop di ret ; 32 ------------------------------------------------------------------- bitmaputil: ; Set CallTable ; see the book, basically don't worry (be happy) push cs pop es mov bx, offset the_util_table ret ; 34 ------------------------------------------------------------------- ; input: ; ax = x1 corner ; bx = y1 corner ; cx = x2 corner ; dx = y2 corner ; return: nothing patbar: ; fill the rectangle using current fillstyle and color ; this version of pattern filling uses the following trick to greatly ; improve speed. Instead of calling put_pixel for every pixel to plot ; it does the write to screen memory on its own. What makes it fast is ; that it only has to find the original offset by multipling XSIZE by ; y1. From then on it increments a pointer to find the next location ; in screen memory (not multiplies required after initial one). ; first, make sure x1 < x2 and y1 < y2 cmp ax,cx ; if x1 < x2 jle x_ok xchg ax,cx x_ok: cmp bx,dx ; if y1 < y2 jle y_ok xchg bx,dx y_ok: ; then load the start and end values mov x1,ax mov x2,cx mov y1,bx mov y2,dx cmp active_mode,1 je patbar_mode1 ; different way to calc pixel offset ; Next, setup the starting value of the VGA's hioffset reg push ax push bx push cx mov cx,ax ; load x value mov ax,bx ; load the y value mul xsize ; find the pixel offset add ax,cx ; add inthe x value mov di,ax mov ax,0a000h ; screen memory segment mov es,ax ; calculate ho many screen byte are from right edge of box ; to left edge of box on next row mov ax,max_x sub ax,x2 add ax,x1 mov next_row,ax pop cx pop bx pop ax again0: ; First, lets figure out the fill pattern parameters push ax push cx push si ; the bit/byte pointer are relative to the absolute ; screen position mod 8 mov fill_byte_ptr,bx ; load the current y value mov fill_bit_ptr,ax ; load the current x value and fill_byte_ptr,07h and fill_bit_ptr,07h mov si,fill_byte_ptr; mov al,[offset active_fillpattern + si] mov cx,fill_bit_ptr shl al,cl and al,80h cmp al,80h jne use_background0 ; if not plotting this dot mov dl,current_fill_color ; else load fill color jmp selected0 use_background0: mov dl,current_background_color ; assume background selected0: pop si pop cx pop ax ; this is the slow way, it has to recalute the offset into ; display memory every time it puts a pixel ; push ax ; push bx ; call put_pixel ; pop bx ; pop ax mov es:[di],dl inc di ; advvance screen mapped memory pointer inc ax ; advance in x dir till at right edge cmp ax,x2 jle again0 mov ax,x1 ; reset to left size of rectangle inc bx ; go to down to next row cmp bx,y2 jg fill_done0 ; Since were changing rows we'll find a new hi and low ; the trick is to add the number of bytes from the right side of the ; box to the left side of the box ( note: no multiplies needed ) add di,next_row jmp again0 fill_done0: ret ; end of patbar in mode 0 patbar_mode1: ; first, make sure x1 < x2 and y1 < y2 call unlock_vga ; keep vga ready to plot pixels ; Next, setup the starting value of the VGA's hioffset reg push ax push bx push cx mov cx,ax ; load x value mov ax,bx ; load the y value call calc_hilow ; find the values of hio and lowo mov ax,bx ; load hio call set_hioffset ; set the vga register for the hioffset mov ax,0a000h ; screen memory segment mov es,ax ; calculate ho many screen byte are from right edge of box ; to left edge of box on next row mov ax,max_x sub ax,x2 add ax,x1 mov next_row,ax pop cx pop bx pop ax again1: ; First, lets figure out the fill pattern parameters push ax push cx push si ; the bit/byte pointer are relative to the absolute ; screen position mod 8 mov fill_byte_ptr,bx ; load the current y value mov fill_bit_ptr,ax ; load the current x value and fill_byte_ptr,07h and fill_bit_ptr,07h mov si,fill_byte_ptr; mov al,[offset active_fillpattern + si] mov cx,fill_bit_ptr shl al,cl and al,80h cmp al,80h jne use_background1 ; if not plotting this dot mov dl,current_fill_color ; else load fill color jmp selected1 use_background1: mov dl,current_background_color ; assume background selected1: pop si pop cx pop ax ; this is the slow way, it has to recalute the offset into ; display memory every time it puts a pixel ; but it shows the easy way to do it. ; push ax ; push bx ; call put_pixel ; pop bx ; pop ax mov es:[di],dl inc di cmp di,0FFFh jle leave_hio1 sub di,1000h inc lasthio push ax ; let vga card know we incremented hi offset mov ax,lasthio call set_hioffset pop ax leave_hio1: inc ax ; advance in x dir till at right edge cmp ax,x2 jle again1 mov ax,x1 ; reset to left size of rectangle inc bx ; go to down to next row cmp bx,y2 jg fill_done1 ;Since were changing rows we'll find a new hi and low ; the trick is to add the number of bytes from the right side of the ; box to the left side of the box ( note: no multiplies needed ) add di,next_row cmp di,0FFFh jle no_bump1 sub di,1000h inc lasthio push ax mov ax,lasthio call set_hioffset pop ax no_bump1: jmp again1 fill_done1: call lock_vga ; protect the vgas register again ret ; 36 ------------------------------------------------------------------ buf_ptr dw ? ; pointer to the buffer for getimage/putimage put_mode db ? ; pixel writing mode for putting images (and,xor,etc) ; NOTE: the book has mistakes in input/output parameters ; input: ; es:bx = pointer to memory buffer ; buffer[0] = window width in x direction ; buffer[2] = window height in y direction ; cx = x1 corner of window ; dx = y1 corner of window ; return: nothing get_image: ; GetImage ; this version of pattern filling uses the following trick to greatly ; improve speed. Instead of calling put_pixel for every pixel to plot ; it does the write to screen memory on its own. What makes it fast is ; that it only has to find the original offset by multipling XSIZE by ; y1. From then on it increments a pointer to find the next location ; in screen memory. mov buf_ptr,bx ; pointer to the buffer where we'll store image mov ax,es:[bx] add ax,cx ; add x1 plus xsize of image window mov bx,es:[bx +2] add bx,dx ; add y1 plus ysize of image window mov x1,cx mov y1,dx mov x2,ax mov y2,bx mov bx,buf_ptr add bx,4 ; first word was used for xsize dimension mov buf_ptr,bx mov ax,x1 mov bx,y1 cmp active_mode,1 je getimage_mode1 ; different way to calc pixel offset ; Next, setup the starting value of the VGA's hioffset reg push ax push bx push cx mov cx,ax ; load x value mov ax,bx ; load the y value mul xsize ; find the pixel offset add ax,cx ; add inthe x value mov di,ax ; calculate ho many screen byte are from right edge of box ; to left edge of box on next row mov ax,max_x sub ax,x2 add ax,x1 mov next_row,ax pop cx pop bx pop ax g_again0: ; move a byte from the screen to the image buffer push ax push es mov ax,0A000h mov es,ax mov al,byte ptr es:[di] pop es push bx mov bx,buf_ptr mov byte ptr es:[bx],al pop bx pop ax inc buf_ptr ; point to next byte in the users buffer inc di ; advvance screen mapped memory pointer inc ax ; advance in x dir till at right edge cmp ax,x2 jle g_again0 mov ax,x1 ; reset to left size of rectangle inc bx ; go to down to next row cmp bx,y2 jg geti_done0 ;Since were changing rows we'll find a new hi and low ;the trick is to add the number of bytes from the right side of the ; box to the left side of the box ( note: no multiplies needed ) add di,next_row jmp g_again0 geti_done0: ret ; end of patbar in mode 0 getimage_mode1: ; first, make sure x1 < x2 and y1 < y2 call unlock_vga ; keep vga ready to plot pixels ; Next, setup the starting value of the VGA's hioffset reg push ax push bx push cx mov cx,ax ; load x value mov ax,bx ; load the y value call calc_hilow ; find the values of hio and lowo mov ax,bx ; load hio call set_hioffset ; set the vga register for the hioffset ; calculate ho many screen byte are from right edge of box ; to left edge of box on next row mov ax,max_x sub ax,x2 add ax,x1 mov next_row,ax pop cx pop bx pop ax g_again1: ; move a byte from the screen to the image buffer push ax push es mov ax,0A000h mov es,ax mov al,byte ptr es:[di] pop es push bx mov bx,buf_ptr mov byte ptr es:[bx],al pop bx pop ax inc buf_ptr ; point to next byte in the users buffer inc di cmp di,0FFFh jle g_leave_hio1 sub di,1000h inc lasthio push ax ; let vga card know we incremented hi offset mov ax,lasthio call set_hioffset pop ax g_leave_hio1: inc ax ; advance in x dir till at right edge cmp ax,x2 jle g_again1 mov ax,x1 ; reset to left size of rectangle inc bx ; go to down to next row cmp bx,y2 jg geti_done1 ;Since were changing rows we'll find a new hi and low ;the trick is to add the number of bytes from the right side of the ; box to the left side of the box ( note: no multiplies needed ) add di,next_row cmp di,0FFFh jle g_no_bump1 sub di,1000h inc lasthio push ax mov ax,lasthio call set_hioffset pop ax g_no_bump1: jmp g_again1 geti_done1: call lock_vga ; protect the vgas register again ret ; 38 ------------------------------------------------------------------------- ; NOTE: the book has mistakes in input/output parameters ; input: ; es:bx = pointer to memory buffer ; buffer[0] = window width in x direction ; buffer[2] = window height in y direction ; cx = x1 corner of window ; dx = y1 corner of window ; return: nothing put_image: ; putImage ; this version of pattern filling uses the following trick to greatly ; improve speed. Instead of calling put_pixel for every pixel to plot ; it does the write to screen memory on its own. What makes it fast is ; that it only has to find the original offset by multipling XSIZE by ; y1. From then on it increments a pointer to find the next location ; in screen memory. mov put_mode,al ; save the put image pixel writing mode mov buf_ptr,bx ; pointer to the buffer where we'll store image mov ax,es:[bx] add ax,cx ; add x1 plut xsize of image window mov bx,es:[bx +2] ; mov bx,di add bx,dx ; add y1 plus ysize of image window mov x1,cx mov y1,dx mov x2,ax mov y2,bx mov bx,buf_ptr add bx,4 ; first word was used for xsize dimension mov buf_ptr,bx mov ax,x1 mov bx,y1 cmp active_mode,1 je putimage_mode1 ; different way to calc pixel offset ; Next, setup the starting value of the VGA's hioffset reg push ax push bx push cx mov cx,ax ; load x value mov ax,bx ; load the y value mul xsize ; find the pixel offset add ax,cx ; add inthe x value mov di,ax ; calculate ho many screen byte are from right edge of box ; to left edge of box on next row mov ax,max_x sub ax,x2 add ax,x1 mov next_row,ax pop cx pop bx pop ax mov cl,put_mode ; keep the put_mode in cl for access speed p_again0: call buf_to_screen inc di ; advvance screen mapped memory pointer inc ax ; advance in x dir till at right edge cmp ax,x2 jle p_again0 mov ax,x1 ; reset to left size of rectangle inc bx ; go to down to next row cmp bx,y2 jg puti_done0 ;Since were changing rows we'll find a new hi and low ; the trick is to add the number of bytes from the right side of the ; box to the left side of the box ( note: no multiplies needed ) add di,next_row jmp p_again0 puti_done0: ret ; end of patbar in mode 0 ; ----------------- putimage for mode 1 ------------------ putimage_mode1: ; first, make sure x1 < x2 and y1 < y2 call unlock_vga ; keep vga ready to plot pixels ; Next, setup the starting value of the VGA's hioffset reg push ax push bx push cx mov cx,ax ; load x value mov ax,bx ; load the y value call calc_hilow ; find the values of hio and lowo mov ax,bx ; load hio call set_hioffset ; set the vga register for the hioffset ; calculate ho many screen byte are from right edge of box ; to left edge of box on next row mov ax,max_x sub ax,x2 add ax,x1 mov next_row,ax pop cx pop bx pop ax mov cl,put_mode ; keep the put_mode in cl for access speed p_again1: call buf_to_screen inc di cmp di,0FFFh jle p_leave_hio1 sub di,1000h inc lasthio push ax ; let vga card know we incremented hi offset mov ax,lasthio call set_hioffset pop ax p_leave_hio1: inc ax ; advance in x dir till at right edge cmp ax,x2 jle p_again1 mov ax,x1 ; reset to left size of rectangle inc bx ; go to down to next row cmp bx,y2 jg puti_done1 ;Since were changing rows we'll find a new hi and low ; the trick is to add the number of bytes from the right side of the ; box to the left side of the box ( note: no multiplies needed ) add di,next_row cmp di,0FFFh jle p_no_bump1 sub di,1000h inc lasthio push ax mov ax,lasthio call set_hioffset pop ax p_no_bump1: jmp p_again1 puti_done1: call lock_vga ; protect the vgas register again ret ; ---------------------------------------------- ; this code write the next byte from the put image buffer ; to the display screen buf_to_screen proc near ; move a byte from the image buffer to the screen push ax push bx ; get the byte out of the users buffer mov bx,buf_ptr mov al,byte ptr es:[bx] pop bx push es push ax mov ax,0A000h mov es,ax ; es reg is pointer to VGA screen pop ax ; write the byte to screen mem according to users mode ; mov cl,put_mode cmp cl,0 jne try_xor0 mov byte ptr es:[di],al jmp mode_found0 try_xor0: cmp cl,1 jne try_or0 xor byte ptr es:[di],al jmp mode_found0 try_or0: cmp cl,2 jne try_and0 or byte ptr es:[di],al jmp mode_found0 try_and0: cmp cl,3 jne then_comp0 and byte ptr es:[di],al jmp mode_found0 then_comp0: xor al,0FFh ; complement the byte and byte ptr es:[di],al mode_found0: pop es pop ax inc buf_ptr ; point to next byte in the users buffer ret buf_to_screen endp ; 40 ------------------------------------------------------------------- ; input: ; ax = x1 ; bx = y1 ; cx = x2 ; dx = y2 ; return: nothing set_clip: ; Set clipping ; load the limits of the clipping window mov clip_x1,ax mov clip_y1,bx mov clip_x2,cx mov clip_y2,dx ret ; 3A ------------------------------------------------------------------- ; if al = 0 ; input: nothing ; return: ; bx = size of color table ; cx = maximum color number ; if al = 1 ; input: nothing ; return: ; es:bx = pointer to default color table (first byte is size) color_query: ; SetParameters cmp al,0 jnz color_table_q mov bx,256 mov cx,255 ret color_table_q: push cs pop es mov bx,offset color_table ret ; ========================================================================= ; line drawing routine from Assembly language ; primer - The Waite Group - page 339-340 draw_line proc near mov ax,y2 sub ax,y1 mov si,1 jge store_y mov si,-1 neg ax store_y: mov delta_y,ax mov ax,x2 sub ax,x1 mov di,1 jge store_x mov di,-1 neg ax store_x: mov delta_x,ax mov ax,delta_x cmp ax,delta_y jl csteep call easy jmp finish_line csteep: call steep finish_line: ret draw_line endp ; --------------------------------- easy proc near mov ax,delta_x shr ax,1 mov halfx,ax mov cx,x1 mov dx,y1 mov bx,0 mov ax,delta_x mov count,ax newdot: call dotplot add cx,di add bx,delta_y cmp bx,halfx jle dcount sub bx,delta_x add dx,si dcount: dec count jge newdot ret easy endp ; -------------------------------- steep proc near mov ax,delta_y shr ax,1 mov halfy,ax mov cx,x1 mov dx,y1 mov bx,0 mov ax,delta_y mov count,ax newdot2: call dotplot add dx,si add bx,delta_x cmp bx,halfy jle dcount2 sub bx,delta_y add cx,di dcount2: dec count jge newdot2 ret steep endp ; ------------------------------ dotplot proc near push ax ; save old values push bx push cx push dx ; check to see if line style pattern dictates a plot or not ; if active_bit_pattern[line_bit_ptr] = 0 then dont plot a dot push ax push cx mov ax,active_linestyle mov cl,line_bit_ptr shl ax,cl and ax,8000h cmp ax,8000h pop cx pop ax mov ax,cx mov bx,dx jne no_dot_plot mov dl,current_line_color ; do plot dot, so load line color call put_pixel ; call the pixel writing routine no_dot_plot: inc line_bit_ptr ; move to next bit in pattern cmp line_bit_ptr,16 ; are we at the end of the pattern jne no_reset_linestyle ; if not do nothing mov line_bit_ptr,0 ; else start over at the pattern beginning no_reset_linestyle: pop dx ; save old values pop cx pop bx pop ax ret dotplot endp ; -------------------------------------------------------------------------- calc_hilow: ; find hi and low vga offsets for x,y location ; cx is column (x) , ax is row (y) ; uses value in varible 'xsize' ; results are lowoffset in di and hioffset in bx push dx mul xsize ; offset =(xsize * y) add ax,cx ; + x jnc nocarry_o inc dx ; (DxAx + Cx) nocarry_o: mov di,ax and di,0FFFh ; low offset is lowest 12 bits of offset mov cl,12 ; (hioffset = offset >> 12 shr ax,cl mov cl,4 shl dx,cl or dx,ax mov bx,dx ; hioffset saved in bx pop dx ret ; --------------------------------------------------------------------------- unlock_vga: ; unlock vga registers push ax push dx mov dx,03CEh ; vga I/O port mov ax,050Fh out dx,ax ; unlock vga registers pop dx pop ax ret ; --------------------------------------------------------------------------- lock_vga: ; lock vga registers push ax push dx mov dx,03CEh ; vga I/O port mov ax,000Fh out dx,ax ; unlock vga registers pop dx pop ax ret ; --------------------------------------------------------------------------- set_hioffset: ; sets the vga hi offset register ; ax contains the value for HIoffset push dx mov lasthio,ax mov ah,al ; hioffset goes in ah mov al,9 ; function code to set PR0A register mov dx,03CEh ; vga I/O address out dx,ax ; set the hioffset value pop dx ret ; --------------------------------------------------------------------------- ; . endofcode label byte ; This is the label ref'd in ACODE segment CODE ENDS END -- Gene Lee UUCP: ...!amdahl!ems!minnow!lee Unisys Corporation Phone: +1 612 635 3993 CSNET: lee@minnow.SP.Unisys.Com If not for the courage of the fearless manager, the paycheck would be lost.