'=========================================================================== ' Subject: VGA 640X480 PANNING Date: 10-04-96 (09:52) ' Author: Douglas H. Lusher Code: QB, QBasic, PDS ' Origin: comp.lang.basic.misc Packet: EGAVGA.ABC '=========================================================================== ' Greetings, fellow BASIC programmers. ' I just finished working out how to do pixel-by-pixel scrolling ' on a VGA card and I thought that this might interest some of ' you, so here's a quick demo. What this code does is this: it ' creates a virtual screen much larger than the usual SCREEN 12 ' 640x480, then prints graphics to this larger virtual screen. ' When you press a key, the graphics stop being printed and you ' can use the arrow keys to pan around and observe different ' parts of the virtual screen. ' When the VGA card sets up a screen mode, the Offset Register ' &H3D5, index &H13 contains the amount of display memory to be ' used per horizontal scan line. The virtual screen is made wider ' than the ordinary screen by increasing this value. The Start ' Address High and Start Address Low registers, &H3D5 index &HC ' & &HD contain the MSB and LSB respectively of the address in ' video memory where the displayed screen begins. Increasing or ' decreasing this value by 1 pans the screen left or right by ' 8 pixels. Changing this value by the amount of memory per scan ' line pans the screen up or down. Notice that horizontal panning ' by this method alone moves the screen 8 pixels at a time. To ' pan horizontally a pixel at a time involves the Horizontal ' PEL Panning register &H3C0 index &H13. Rotating the values of ' this register from 0 through 7 pans the screen pixel-column ' by pixel-column. Note that this register must only be changed ' during a vertical retrace and that an INP(&H3DA) command must ' preceed a change to this register. Also, bit 5 must always be ' set. ' It is worth noting also that the normal BASIC graphics commands ' will not work on the larger virtual screen, so I have provided ' my own code to create the graphics in this demo. While the code ' for these graphics commands is good, it is just BASIC and is ' not necessarily optimised, so I realized that it is slow. That ' particular aspect is not the focus of this demo. DECLARE SUB QPrint (X%, Y%, Text$, Culler%) DECLARE SUB DrawCircle (X%, Y%, Radius%, Culler%) DECLARE SUB PutPixel (X%, Y%, Culler%) DECLARE SUB DrawLine (X1%, Y1%, X2%, Y2%, Culler%) DEFINT A-Z REDIM BitMask%(0 TO 7) Mask% = 128 FOR Bit% = 0 TO 7 BitMask%(Bit%) = Mask%: Mask% = Mask% \ 2 NEXT RANDOMIZE TIMER SCREEN 12: ScrnWid% = 640: ScrnHgt% = 480 'the width of the virtual screen in pixels: VScrnWid% = 832 'In SCREEN 12, the following must be true: ' VScrnWid% must be equal to or greater than 640 ' VScrnWid% must be less than or equal to 1088 ' VScrnWid% must be evenly divisible by 16 HPanLimit% = VScrnWid% - ScrnWid% BytesPerLine% = VScrnWid% \ 8 'the height of the virtual screen in pixels: VScrnHgt% = 624 'In SCREEN 12, the following must be true: ' VScrnHgt% must be equal to or greater than 480 ' VScrnHgt% must be less than or equal to 819 ' The product of VScrnHgt% multiplied by VScrnWid% must be less ' than or equal to 524288 VPanLimit% = VScrnHgt% - ScrnHgt% 'create the virtual screen: OUT &H3D4, &H13: OUT &H3D5, VScrnWid% \ 16 'put up some graphics MinX% = 0: MaxX% = VScrnWid% - 1 MinY% = 0: MaxY% = VScrnHgt% - 1 CALL DrawLine(MinX%, MinY%, MaxX%, MinY%, 1) CALL DrawLine(MaxX%, MinY%, MaxX%, MaxY%, 2) CALL DrawLine(MaxX%, MaxY%, MinX%, MaxY%, 3) CALL DrawLine(MinX%, MaxY%, MinX%, MinY%, 4) RR% = VScrnHgt%: IF VScrnWid% < VScrnHgt% THEN RR% = VScrnWid% DO UNTIL LEN(INKEY$) X1% = INT(RND * VScrnWid%) Y1% = INT(RND * VScrnHgt%) X2% = INT(RND * VScrnWid%) Y2% = INT(RND * VScrnHgt%) C% = INT(RND * 14) + 1 CALL DrawLine(X1%, Y1%, X2%, Y2%, C%) R% = INT(RND * (RR% \ 4)) X1% = INT(RND * (VScrnWid% - R% * 2)) + R% Y1% = INT(RND * (VScrnHgt% - R% * 2)) + R% C% = INT(RND * 14) + 1 CALL DrawCircle(X1%, Y1%, R%, C%) LOOP CALL QPrint(8, 8, "Upper Left", 15) CALL QPrint(VScrnWid% - 96, 8, "Upper Right", 15) CALL QPrint(8, VScrnHgt% - 16, "Lower Left", 15) CALL QPrint(VScrnWid% - 96, VScrnHgt% - 16, "Lower Right", 15) 'allow the user to pan around the virtual screen X% = 0: Y% = 0 DO DO WHILE LEN(INKEY$): LOOP DO: KP$ = INKEY$: LOOP UNTIL LEN(KP$) KP% = ASC(KP$): IF KP% = 0 THEN KP% = -ASC(MID$(KP$, 2)) SELECT CASE KP% CASE 27 EXIT DO CASE 52, -75 'Four, LArrow IF X% > 0 THEN X% = X% - 1 DO: LOOP WHILE (INP(&H3DA) AND 8) = 0 OUT &H3C0, &H33: OUT &H3C0, X% MOD 8 ELSE BEEP END IF CASE 54, -77 'Six, RArrow IF X% < HPanLimit% THEN X% = X% + 1 DO: LOOP WHILE (INP(&H3DA) AND 8) = 0 OUT &H3C0, &H33: OUT &H3C0, X% MOD 8 ELSE BEEP END IF CASE 56, -72 'Eight, UpArrow IF Y% > 0 THEN Y% = Y% - 1 ELSE BEEP END IF CASE 50, -80 'Two, DnArrow IF Y% < VPanLimit% THEN Y% = Y% + 1 ELSE BEEP END IF END SELECT Ptr% = (Y% * BytesPerLine%) + (X% \ 8) OUT &H3D4, &HD: OUT &H3D5, Ptr% MOD 256 OUT &H3D4, &HC: OUT &H3D5, Ptr% \ 256 LOOP SCREEN 0: WIDTH 80 END SUB DrawCircle (X%, Y%, Radius%, Culler%) 'a routine to draw circles using only integers and integer math 'an implementation of Bresenham's algorithm 'by Douglas H. Lusher, 05-09-1996 A% = 0 B% = Radius% D% = (1 - Radius%) * 2 XX1% = X%: YY1% = Y% + B% XX2% = X%: YY2% = Y% - B% XY1% = X% + B%: YX1% = Y% XY2% = X% - B%: YX2% = Y% DO WHILE B% >= A% CALL PutPixel(XX1%, YY1%, Culler%) CALL PutPixel(XX1%, YY2%, Culler%) CALL PutPixel(XX2%, YY1%, Culler%) CALL PutPixel(XX2%, YY2%, Culler%) CALL PutPixel(XY1%, YX1%, Culler%) CALL PutPixel(XY1%, YX2%, Culler%) CALL PutPixel(XY2%, YX1%, Culler%) CALL PutPixel(XY2%, YX2%, Culler%) IF D% + B% > 0 THEN B% = B% - 1 D% = D% - (B% * 2) + 1 YY1% = YY1% - 1: YY2% = YY2% + 1 XY1% = XY1% - 1: XY2% = XY2% + 1 END IF IF A% > D% THEN A% = A% + 1 D% = D% + (A% * 2) + 1 XX1% = XX1% + 1: XX2% = XX2% - 1 YX1% = YX1% + 1: YX2% = YX2% - 1 END IF LOOP END SUB SUB DrawLine (X1%, Y1%, X2%, Y2%, Culler%) 'a routine to draw lines using only integers and integer math 'an implementation of Bresenham's algorithm 'by Douglas H. Lusher, 05-08-1996 A% = X2% - X1% B% = Y2% - Y1% DX2% = 1: DY2% = 1 IF A% < 0 THEN A% = -A%: DX2% = -1 IF B% < 0 THEN B% = -B%: DY2% = -1 DX1% = DX2%: DY1% = 0 IF A% < B% THEN SWAP A%, B%: DX1% = 0: DY1% = DY2% I1% = B% * 2 D% = I1% - A% I2% = D% - A% X% = X1%: Y% = Y1% FOR I% = 0 TO A% CALL PutPixel(X%, Y%, Culler%) IF D% < 0 THEN X% = X% + DX1% Y% = Y% + DY1% D% = D% + I1% ELSE X% = X% + DX2% Y% = Y% + DY2% D% = D% + I2% END IF NEXT END SUB SUB PutPixel (X%, Y%, Culler%) SHARED BitMask%(), BytesPerLine% DEF SEG = &HA000 Offset& = (CLNG(Y%) * BytesPerLine%) + (X% \ 8) OUT &H3CE, 5: OUT &H3CF, 2 OUT &H3CE, 8: OUT &H3CF, BitMask%(X% AND 7) Byte% = PEEK(Offset&): POKE Offset&, Culler% END SUB SUB QPrint (X%, Y%, Text$, Culler%) 'this routine uses the VGA hardware to print text ' in 16 color graphics modes 'by Douglas H. Lusher, 06-08-1996 SHARED BytesPerLine% ' 8 x 8 char box, CGA CharSegment% = &HFFA6: CharOffset% = &HE: CharHgt% = 8 ' 8 x 16 char box, VGA 'DIM Regs AS RegTypeX 'Regs.AX = &H1130 'Regs.BX = &H600 'CALL InterruptX(&H10, Regs, Regs) 'CharSegment% = Regs.ES: CharOffset% = Regs.BP: CharHgt% = 16 REDIM BitPattern%(1 TO CharHgt%) Temp& = (CLNG(Y%) * BytesPerLine%) + (X% \ 8) VideoSegment% = &HA000 + (Temp& \ 16) VideoOffset% = (Temp& MOD 16) - 1 OUT &H3CE, 5: OUT &H3CF, 2: OUT &H3CE, 8 FOR Char% = 1 TO LEN(Text$) Ptr% = CharHgt% * ASC(MID$(Text$, Char%, 1)) + CharOffset% - 1 DEF SEG = CharSegment% FOR Ln% = 1 TO CharHgt% BitPattern%(Ln%) = PEEK(Ptr% + Ln%) NEXT VideoPtr% = VideoOffset% + Char% DEF SEG = VideoSegment% FOR Ln% = 1 TO CharHgt% OUT &H3CF, BitPattern%(Ln%) Byte% = PEEK(VideoPtr%): POKE VideoPtr%, Culler% VideoPtr% = VideoPtr% + BytesPerLine% NEXT NEXT END SUB