diff --git a/tests/pc/vga/L48-1.C b/tests/pc/vga/L48-1.C new file mode 100644 index 000000000..d9f3532e9 --- /dev/null +++ b/tests/pc/vga/L48-1.C @@ -0,0 +1,45 @@ +/* Program to demonstrate Mode X (320x240, 256 colors) patterned + rectangle fills by filling the screen with adjacent 80x60 + rectangles in a variety of patterns. Tested with Borland C++ + in C compilation mode and the small model */ +*/ +#include +#include + +void Set320x240Mode(void); +void FillPatternX(int, int, int, int, unsigned int, char*); + +/* 16 4x4 patterns */ +static char Patt0[]={10,0,10,0,0,10,0,10,10,0,10,0,0,10,0,10}; +static char Patt1[]={9,0,0,0,0,9,0,0,0,0,9,0,0,0,0,9}; +static char Patt2[]={5,0,0,0,0,0,5,0,5,0,0,0,0,0,5,0}; +static char Patt3[]={14,0,0,14,0,14,14,0,0,14,14,0,14,0,0,14}; +static char Patt4[]={15,15,15,1,15,15,1,1,15,1,1,1,1,1,1,1}; +static char Patt5[]={12,12,12,12,6,6,6,12,6,6,6,12,6,6,6,12}; +static char Patt6[]={80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,15}; +static char Patt7[]={78,78,78,78,80,80,80,80,82,82,82,82,84,84,84,84}; +static char Patt8[]={78,80,82,84,80,82,84,78,82,84,78,80,84,78,80,82}; +static char Patt9[]={78,80,82,84,78,80,82,84,78,80,82,84,78,80,82,84}; +static char Patt10[]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; +static char Patt11[]={0,1,2,3,0,1,2,3,0,1,2,3,0,1,2,3}; +static char Patt12[]={14,14,9,9,14,9,9,14,9,9,14,14,9,14,14,9}; +static char Patt13[]={15,8,8,8,15,15,15,8,15,15,15,8,15,8,8,8}; +static char Patt14[]={3,3,3,3,3,7,7,3,3,7,7,3,3,3,3,3}; +static char Patt15[]={0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,89}; +/* Table of pointers to the 16 4x4 patterns with which to draw */ +static char* PattTable[] = {Patt0,Patt1,Patt2,Patt3,Patt4,Patt5,Patt6, + Patt7,Patt8,Patt9,Patt10,Patt11,Patt12,Patt13,Patt14,Patt15}; +void main() { + int i,j; + union REGS regset; + + Set320x240Mode(); + for (j = 0; j < 4; j++) { + for (i = 0; i < 4; i++) { + FillPatternX(i*80,j*60,i*80+80,j*60+60,0,PattTable[j*4+i]); + } + } + getch(); + regset.x.ax = 0x0003; /* switch back to text mode and done */ + int86(0x10, ®set, ®set); +} diff --git a/tests/pc/vga/L48-2.ASM b/tests/pc/vga/L48-2.ASM new file mode 100644 index 000000000..427430847 --- /dev/null +++ b/tests/pc/vga/L48-2.ASM @@ -0,0 +1,174 @@ +; Mode X (320x240, 256 colors) rectangle 4x4 pattern fill routine. +; Upper left corner of pattern is always aligned to a multiple-of-4 +; row and column. Works on all VGAs. Uses approach of copying the +; pattern to off-screen display memory, then loading the latches with +; the pattern for each scan line and filling each scan line four +; pixels at a time. Fills up to but not including the column at EndX +; and the row at EndY. No clipping is performed. All ASM code tested +; with TASM. C near-callable as: +; +; void FillPatternX(int StartX, int StartY, int EndX, int EndY, +; unsigned int PageBase, char* Pattern); + +SC_INDEX equ 03c4h ;Sequence Controller Index register port +MAP_MASK equ 02h ;index in SC of Map Mask register +GC_INDEX equ 03ceh ;Graphics Controller Index register port +BIT_MASK equ 08h ;index in GC of Bit Mask register +PATTERN_BUFFER equ 0fffch ;offset in screen memory of the buffer used + ; to store each pattern during drawing +SCREEN_SEG equ 0a000h ;segment of display memory in Mode X +SCREEN_WIDTH equ 80 ;width of screen in addresses from one scan + ; line to the next +parms struc + dw 2 dup (?) ;pushed BP and return address +StartX dw ? ;X coordinate of upper left corner of rect +StartY dw ? ;Y coordinate of upper left corner of rect +EndX dw ? ;X coordinate of lower right corner of rect + ; (the row at EndX is not filled) +EndY dw ? ;Y coordinate of lower right corner of rect + ; (the column at EndY is not filled) +PageBase dw ? ;base offset in display memory of page in + ; which to fill rectangle +Pattern dw ? ;4x4 pattern with which to fill rectangle +parms ends + +NextScanOffset equ -2 ;local storage for distance from end of one + ; scan line to start of next +RectAddrWidth equ -4 ;local storage for address width of rectangle +Height equ -6 ;local storage for height of rectangle +STACK_FRAME_SIZE equ 6 + + .model small + .data +; Plane masks for clipping left and right edges of rectangle. +LeftClipPlaneMask db 00fh,00eh,00ch,008h +RightClipPlaneMask db 00fh,001h,003h,007h + .code + public _FillPatternX +_FillPatternX proc near + push bp ;preserve caller's stack frame + mov bp,sp ;point to local stack frame + sub sp,STACK_FRAME_SIZE ;allocate space for local vars + push si ;preserve caller's register variables + push di + + cld + mov ax,SCREEN_SEG ;point ES to display memory + mov es,ax + ;copy pattern to display memory buffer + mov si,[bp+Pattern] ;point to pattern to fill with + mov di,PATTERN_BUFFER ;point ES:DI to pattern buffer + mov dx,SC_INDEX ;point Sequence Controller Index to + mov al,MAP_MASK ; Map Mask + out dx,al + inc dx ;point to SC Data register + mov cx,4 ;4 pixel quadruplets in pattern +DownloadPatternLoop: + mov al,1 ; + out dx,al ;select plane 0 for writes + movsb ;copy over next plane 0 pattern pixel + dec di ;stay at same address for next plane + mov al,2 ; + out dx,al ;select plane 1 for writes + movsb ;copy over next plane 1 pattern pixel + dec di ;stay at same address for next plane + mov al,4 ; + out dx,al ;select plane 2 for writes + movsb ;copy over next plane 2 pattern pixel + dec di ;stay at same address for next plane + mov al,8 ; + out dx,al ;select plane 3 for writes + movsb ;copy over next plane 3 pattern pixel + ; and advance address + loop DownloadPatternLoop + + mov dx,GC_INDEX ;set the bit mask to select all bits + mov ax,00000h+BIT_MASK ; from the latches and none from + out dx,ax ; the CPU, so that we can write the + ; latch contents directly to memory + mov ax,[bp+StartY] ;top rectangle scan line + mov si,ax + and si,011b ;top rect scan line modulo 4 + add si,PATTERN_BUFFER ;point to pattern scan line that + ; maps to top line of rect to draw + mov dx,SCREEN_WIDTH + mul dx ;offset in page of top rectangle scan line + mov di,[bp+StartX] + mov bx,di + shr di,1 ;X/4 = offset of first rectangle pixel in scan + shr di,1 ; line + add di,ax ;offset of first rectangle pixel in page + add di,[bp+PageBase] ;offset of first rectangle pixel in + ; display memory + and bx,0003h ;look up left edge plane mask + mov ah,LeftClipPlaneMask[bx] ; to clip + mov bx,[bp+EndX] + and bx,0003h ;look up right edge plane + mov al,RightClipPlaneMask[bx] ; mask to clip + mov bx,ax ;put the masks in BX + + mov cx,[bp+EndX] ;calculate # of addresses across rect + mov ax,[bp+StartX] + cmp cx,ax + jle FillDone ;skip if 0 or negative width + dec cx + and ax,not 011b + sub cx,ax + shr cx,1 + shr cx,1 ;# of addresses across rectangle to fill - 1 + jnz MasksSet ;there's more than one pixel to draw + and bh,bl ;there's only one pixel, so combine the left + ; and right edge clip masks +MasksSet: + mov ax,[bp+EndY] + sub ax,[bp+StartY] ;AX = height of rectangle + jle FillDone ;skip if 0 or negative height + mov [bp+Height],ax + mov ax,SCREEN_WIDTH + sub ax,cx ;distance from end of one scan line to start + dec ax ; of next + mov [bp+NextScanOffset],ax + mov [bp+RectAddrWidth],cx ;remember width in addresses - 1 + mov dx,SC_INDEX+1 ;point to Sequence Controller Data reg + ; (SC Index still points to Map Mask) +FillRowsLoop: + mov cx,[bp+RectAddrWidth] ;width across - 1 + mov al,es:[si] ;read display memory to latch this scan + ; line's pattern + inc si ;point to the next pattern scan line, wrapping + jnz short NoWrap ; back to the start of the pattern if + sub si,4 ; we've run off the end +NoWrap: + mov al,bh ;put left-edge clip mask in AL + out dx,al ;set the left-edge plane (clip) mask + stosb ;draw the left edge (pixels come from latches; + ; value written by CPU doesn't matter) + dec cx ;count off left edge address + js FillLoopBottom ;that's the only address + jz DoRightEdge ;there are only two addresses + mov al,00fh ;middle addresses are drawn 4 pixels at a pop + out dx,al ;set the middle pixel mask to no clip + rep stosb ;draw the middle addresses four pixels apiece + ; (from latches; value written doesn't matter) +DoRightEdge: + mov al,bl ;put right-edge clip mask in AL + out dx,al ;set the right-edge plane (clip) mask + stosb ;draw the right edge (from latches; value + ; written doesn't matter) +FillLoopBottom: + add di,[bp+NextScanOffset] ;point to the start of the next scan + ; line of the rectangle + dec word ptr [bp+Height] ;count down scan lines + jnz FillRowsLoop +FillDone: + mov dx,GC_INDEX+1 ;restore the bit mask to its default, + mov al,0ffh ; which selects all bits from the CPU + out dx,al ; and none from the latches (the GC + ; Index still points to Bit Mask) + pop di ;restore caller's register variables + pop si + mov sp,bp ;discard storage for local variables + pop bp ;restore caller's stack frame + ret +_FillPatternX endp + end diff --git a/tests/pc/vga/L48-3.ASM b/tests/pc/vga/L48-3.ASM new file mode 100644 index 000000000..e54ce3bdc --- /dev/null +++ b/tests/pc/vga/L48-3.ASM @@ -0,0 +1,173 @@ +; Mode X (320x240, 256 colors) display memory to display memory copy +; routine. Left edge of source rectangle modulo 4 must equal left edge +; of destination rectangle modulo 4. Works on all VGAs. Uses approach +; of reading 4 pixels at a time from the source into the latches, then +; writing the latches to the destination. Copies up to but not +; including the column at SourceEndX and the row at SourceEndY. No +; clipping is performed. Results are not guaranteed if the source and +; destination overlap. C near-callable as: +; +; void CopyScreenToScreenX(int SourceStartX, int SourceStartY, +; int SourceEndX, int SourceEndY, int DestStartX, +; int DestStartY, unsigned int SourcePageBase, +; unsigned int DestPageBase, int SourceBitmapWidth, +; int DestBitmapWidth); + +SC_INDEX equ 03c4h ;Sequence Controller Index register port +MAP_MASK equ 02h ;index in SC of Map Mask register +GC_INDEX equ 03ceh ;Graphics Controller Index register port +BIT_MASK equ 08h ;index in GC of Bit Mask register +SCREEN_SEG equ 0a000h ;segment of display memory in Mode X + +parms struc + dw 2 dup (?) ;pushed BP and return address +SourceStartX dw ? ;X coordinate of upper left corner of source +SourceStartY dw ? ;Y coordinate of upper left corner of source +SourceEndX dw ? ;X coordinate of lower right corner of source + ; (the row at SourceEndX is not copied) +SourceEndY dw ? ;Y coordinate of lower right corner of source + ; (the column at SourceEndY is not copied) +DestStartX dw ? ;X coordinate of upper left corner of dest +DestStartY dw ? ;Y coordinate of upper left corner of dest +SourcePageBase dw ? ;base offset in display memory of page in + ; which source resides +DestPageBase dw ? ;base offset in display memory of page in + ; which dest resides +SourceBitmapWidth dw ? ;# of pixels across source bitmap + ; (must be a multiple of 4) +DestBitmapWidth dw ? ;# of pixels across dest bitmap + ; (must be a multiple of 4) +parms ends + +SourceNextScanOffset equ -2 ;local storage for distance from end of + ; one source scan line to start of next +DestNextScanOffset equ -4 ;local storage for distance from end of + ; one dest scan line to start of next +RectAddrWidth equ -6 ;local storage for address width of rectangle +Height equ -8 ;local storage for height of rectangle +STACK_FRAME_SIZE equ 8 + + .model small + .data +; Plane masks for clipping left and right edges of rectangle. +LeftClipPlaneMask db 00fh,00eh,00ch,008h +RightClipPlaneMask db 00fh,001h,003h,007h + .code + public _CopyScreenToScreenX +_CopyScreenToScreenX proc near + push bp ;preserve caller's stack frame + mov bp,sp ;point to local stack frame + sub sp,STACK_FRAME_SIZE ;allocate space for local vars + push si ;preserve caller's register variables + push di + push ds + + cld + mov dx,GC_INDEX ;set the bit mask to select all bits + mov ax,00000h+BIT_MASK ; from the latches and none from + out dx,ax ; the CPU, so that we can write the + ; latch contents directly to memory + mov ax,SCREEN_SEG ;point ES to display memory + mov es,ax + mov ax,[bp+DestBitmapWidth] + shr ax,1 ;convert to width in addresses + shr ax,1 + mul [bp+DestStartY] ;top dest rect scan line + mov di,[bp+DestStartX] + shr di,1 ;X/4 = offset of first dest rect pixel in + shr di,1 ; scan line + add di,ax ;offset of first dest rect pixel in page + add di,[bp+DestPageBase] ;offset of first dest rect pixel + ; in display memory + mov ax,[bp+SourceBitmapWidth] + shr ax,1 ;convert to width in addresses + shr ax,1 + mul [bp+SourceStartY] ;top source rect scan line + mov si,[bp+SourceStartX] + mov bx,si + shr si,1 ;X/4 = offset of first source rect pixel in + shr si,1 ; scan line + add si,ax ;offset of first source rect pixel in page + add si,[bp+SourcePageBase] ;offset of first source rect + ; pixel in display memory + and bx,0003h ;look up left edge plane mask + mov ah,LeftClipPlaneMask[bx]; to clip + mov bx,[bp+SourceEndX] + and bx,0003h ;look up right edge plane + mov al,RightClipPlaneMask[bx] ; mask to clip + mov bx,ax ;put the masks in BX + + mov cx,[bp+SourceEndX] ;calculate # of addresses across + mov ax,[bp+SourceStartX] ; rect + cmp cx,ax + jle CopyDone ;skip if 0 or negative width + dec cx + and ax,not 011b + sub cx,ax + shr cx,1 + shr cx,1 ;# of addresses across rectangle to copy - 1 + jnz MasksSet ;there's more than one address to draw + and bh,bl ;there's only one address, so combine the left + ; and right edge clip masks +MasksSet: + mov ax,[bp+SourceEndY] + sub ax,[bp+SourceStartY] ;AX = height of rectangle + jle CopyDone ;skip if 0 or negative height + mov [bp+Height],ax + mov ax,[bp+DestBitmapWidth] + shr ax,1 ;convert to width in addresses + shr ax,1 + sub ax,cx ;distance from end of one dest scan line to + dec ax ; start of next + mov [bp+DestNextScanOffset],ax + mov ax,[bp+SourceBitmapWidth] + shr ax,1 ;convert to width in addresses + shr ax,1 + sub ax,cx ;distance from end of one source scan line to + dec ax ; start of next + mov [bp+SourceNextScanOffset],ax + mov [bp+RectAddrWidth],cx ;remember width in addresses - 1 +;-----------------------BUG FIX + mov dx,SC_INDEX + mov al,MAP_MASK + out dx,al ;point SC Index reg to Map Mask + inc dx ;point to SC Data reg +;-----------------------BUG FIX + mov ax,es ;DS=ES=screen segment for MOVS + mov ds,ax +CopyRowsLoop: + mov cx,[bp+RectAddrWidth] ;width across - 1 + mov al,bh ;put left-edge clip mask in AL + out dx,al ;set the left-edge plane (clip) mask + movsb ;copy the left edge (pixels go through + ; latches) + dec cx ;count off left edge address + js CopyLoopBottom ;that's the only address + jz DoRightEdge ;there are only two addresses + mov al,00fh ;middle addresses are drawn 4 pixels at a pop + out dx,al ;set the middle pixel mask to no clip + rep movsb ;draw the middle addresses four pixels apiece + ; (pixels copied through latches) +DoRightEdge: + mov al,bl ;put right-edge clip mask in AL + out dx,al ;set the right-edge plane (clip) mask + movsb ;draw the right edge (pixels copied through + ; latches) +CopyLoopBottom: + add si,[bp+SourceNextScanOffset] ;point to the start of + add di,[bp+DestNextScanOffset] ; next source & dest lines + dec word ptr [bp+Height] ;count down scan lines + jnz CopyRowsLoop +CopyDone: + mov dx,GC_INDEX+1 ;restore the bit mask to its default, + mov al,0ffh ; which selects all bits from the CPU + out dx,al ; and none from the latches (the GC + ; Index still points to Bit Mask) + pop ds + pop di ;restore caller's register variables + pop si + mov sp,bp ;discard storage for local variables + pop bp ;restore caller's stack frame + ret +_CopyScreenToScreenX endp + end diff --git a/tests/pc/vga/L48-4.ASM b/tests/pc/vga/L48-4.ASM new file mode 100644 index 000000000..f7e2f9e5c --- /dev/null +++ b/tests/pc/vga/L48-4.ASM @@ -0,0 +1,118 @@ +; Mode X (320x240, 256 colors) system memory to display memory copy +; routine. Uses approach of changing the plane for each pixel copied; +; this is slower than copying all pixels in one plane, then all pixels +; in the next plane, and so on, but it is simpler; besides, images for +; which performance is critical should be stored in off-screen memory +; and copied to the screen via the latches. Copies up to but not +; including the column at SourceEndX and the row at SourceEndY. No +; clipping is performed. C near-callable as: +; +; void CopySystemToScreenX(int SourceStartX, int SourceStartY, +; int SourceEndX, int SourceEndY, int DestStartX, +; int DestStartY, char* SourcePtr, unsigned int DestPageBase, +; int SourceBitmapWidth, int DestBitmapWidth); + +SC_INDEX equ 03c4h ;Sequence Controller Index register port +MAP_MASK equ 02h ;index in SC of Map Mask register +SCREEN_SEG equ 0a000h ;segment of display memory in Mode X + +parms struc + dw 2 dup (?) ;pushed BP and return address +SourceStartX dw ? ;X coordinate of upper left corner of source +SourceStartY dw ? ;Y coordinate of upper left corner of source +SourceEndX dw ? ;X coordinate of lower right corner of source + ; (the row at EndX is not copied) +SourceEndY dw ? ;Y coordinate of lower right corner of source + ; (the column at EndY is not copied) +DestStartX dw ? ;X coordinate of upper left corner of dest +DestStartY dw ? ;Y coordinate of upper left corner of dest +SourcePtr dw ? ;pointer in DS to start of bitmap in which + ; source resides +DestPageBase dw ? ;base offset in display memory of page in + ; which dest resides +SourceBitmapWidth dw ? ;# of pixels across source bitmap +DestBitmapWidth dw ? ;# of pixels across dest bitmap + ; (must be a multiple of 4) +parms ends + +RectWidth equ -2 ;local storage for width of rectangle +LeftMask equ -4 ;local storage for left rect edge plane mask +STACK_FRAME_SIZE equ 4 + + .model small + .code + public _CopySystemToScreenX +_CopySystemToScreenX proc near + push bp ;preserve caller's stack frame + mov bp,sp ;point to local stack frame + sub sp,STACK_FRAME_SIZE ;allocate space for local vars + push si ;preserve caller's register variables + push di + + cld + mov ax,SCREEN_SEG ;point ES to display memory + mov es,ax + mov ax,[bp+SourceBitmapWidth] + mul [bp+SourceStartY] ;top source rect scan line + add ax,[bp+SourceStartX] + add ax,[bp+SourcePtr] ;offset of first source rect pixel + mov si,ax ; in DS + + mov ax,[bp+DestBitmapWidth] + shr ax,1 ;convert to width in addresses + shr ax,1 + mov [bp+DestBitmapWidth],ax ;remember address width + mul [bp+DestStartY] ;top dest rect scan line + mov di,[bp+DestStartX] + mov cx,di + shr di,1 ;X/4 = offset of first dest rect pixel in + shr di,1 ; scan line + add di,ax ;offset of first dest rect pixel in page + add di,[bp+DestPageBase] ;offset of first dest rect pixel + ; in display memory + and cl,011b ;CL = first dest pixel's plane + mov al,11h ;upper nibble comes into play when plane wraps + ; from 3 back to 0 + shl al,cl ;set the bit for the first dest pixel's plane + mov [bp+LeftMask],al ; in each nibble to 1 + + mov cx,[bp+SourceEndX] ;calculate # of pixels across + sub cx,[bp+SourceStartX] ; rect + jle CopyDone ;skip if 0 or negative width + mov [bp+RectWidth],cx + mov bx,[bp+SourceEndY] + sub bx,[bp+SourceStartY] ;BX = height of rectangle + jle CopyDone ;skip if 0 or negative height + mov dx,SC_INDEX ;point to SC Index register + mov al,MAP_MASK + out dx,al ;point SC Index reg to the Map Mask + inc dx ;point DX to SC Data reg +CopyRowsLoop: + mov ax,[bp+LeftMask] + mov cx,[bp+RectWidth] + push si ;remember the start offset in the source + push di ;remember the start offset in the dest +CopyScanLineLoop: + out dx,al ;set the plane for this pixel + movsb ;copy the pixel to the screen + rol al,1 ;set mask for next pixel's plane + cmc ;advance destination address only when + sbb di,0 ; wrapping from plane 3 to plane 0 + ; (else undo INC DI done by MOVSB) + loop CopyScanLineLoop + pop di ;retrieve the dest start offset + add di,[bp+DestBitmapWidth] ;point to the start of the + ; next scan line of the dest + pop si ;retrieve the source start offset + add si,[bp+SourceBitmapWidth] ;point to the start of the + ; next scan line of the source + dec bx ;count down scan lines + jnz CopyRowsLoop +CopyDone: + pop di ;restore caller's register variables + pop si + mov sp,bp ;discard storage for local variables + pop bp ;restore caller's stack frame + ret +_CopySystemToScreenX endp + end diff --git a/tests/pc/vga/MAKEFILE b/tests/pc/vga/MAKEFILE index c0a2b370a..07b51a47f 100644 --- a/tests/pc/vga/MAKEFILE +++ b/tests/pc/vga/MAKEFILE @@ -16,7 +16,7 @@ LINK=C:\MSDEV\BIN\WIN95\C816\BIN\LINK.EXE ALL: L23-1.EXE L24-1.EXE L25-1.EXE L25-2.EXE L25-3.EXE L25-4.EXE L26-1.EXE L26-2.EXE L27-1.EXE L27-2.EXE L27-3.EXE \ L28-1.EXE L28-2.EXE L28-3.EXE L29-1.EXE L29-2.EXE L29-3.EXE L29-4.EXE L30-1.EXE L30-2.EXE L31-1.EXE L31-2.EXE \ - L33-1.EXE L34-1.EXE L35-1.EXE L35-3.EXE L47-4.EXE + L33-1.EXE L34-1.EXE L35-1.EXE L35-3.EXE L47-4.EXE L47-5.EXE L47-6.EXE L48-2.EXE L48-3.EXE L48-4.EXE L35-1.EXE: L35-1.OBJ L35-2.OBJ $(LINK) $**,$@; @@ -26,3 +26,18 @@ L35-3.EXE: L35-3.OBJ L35-2.OBJ L47-4.EXE: L47-1.OBJ L47-2.OBJ L47-3.OBJ L47-4.OBJ L47-7.OBJ $(LINK) $**,$@; + +L47-5.EXE: L47-1.OBJ L47-2.OBJ L47-3.OBJ L47-5.OBJ L47-7.OBJ + $(LINK) $**,$@; + +L47-6.EXE: L47-1.OBJ L47-2.OBJ L47-3.OBJ L47-6.OBJ L47-7.OBJ + $(LINK) $**,$@; + +L48-2.EXE: L48-1.OBJ L47-1.OBJ L48-2.OBJ + $(LINK) $**,$@; + +L48-3.EXE: L48-1.OBJ L47-1.OBJ L48-3.OBJ + $(LINK) $**,$@; + +L48-4.EXE: L48-1.OBJ L47-1.OBJ L48-4.OBJ + $(LINK) $**,$@;