'=========================================================================== ' Subject: 320X400X256 VGA MODEX (COMPILE) Date: 06-16-96 (22:49) ' Author: Douglas H. Lusher Code: QB, PDS ' Origin: FidoNet QUIK_BAS Echo Packet: GRAPHICS.ABC '=========================================================================== 'Hello, everyone! 'Here's some code that might be of interest to some of you. 'This code puts the VGA into MODEX - 320x400 with 256 colors. 'I have also supplied code to write pixels, read a pixel, 'clear the screen, print text, save a screen image (complete 'with the palette) to a single file, and restore a saved 'screen and its palette from a file. 'Some of you may have noticed Earl Montgomery posting code 'that would put the VGA into this mode. Be aware that there 'are different ways of arranging video memory in this mode 'and the VGA will not operate the same way using this code 'as it does using his code. 'The advantage of this method is that there are two '(Count 'em! TWO!) video pages available which might be used 'for animation. This code has been written so that you can 'read or write to either page and I have included code that 'lets you flip between them. 'Here is a brief(!) description of the memory arrangements 'when using this code. Since there are 256 colors possible, 'each pixel requires a byte of video memory. On the basic 'VGA card there is 256K of memory arranged as four planes 'of 64K bytes each. In this mode, the first pixel (0,0) is 'the first byte of plane 0; pixel (1,0) is the first byte 'of plane 1; pixel (2,0) is the first byte of plane 2; pixel '(3,0) is the first byte of plane 3; pixel (4,0) is the second 'byte of plane 0; etc. Thus the address of any pixel is given 'by the formula: Address = (Y * 80) + (X \ 4) and the plane 'is: Plane = X MOD 4. To access a pixel for reading or writing, 'one need only select the proper plane and then peek or poke 'at the appropriate address. This makes screen writing in this 'mode a very rapid operation. Since only 32,000 bytes of each 'plane are required under this arrangement, there is plenty of 'room left for a second screen page beginning at offset 32678. 'This is most easily accomplished by using a DEF SEG = &HA800 'for the second page and then addresses remain the same for the 'second page as for the first (which uses DEF SEG = &HA000). '============================================================== 'Note that this code *must* be compiled in order to run. 'This is due to the two SUBs whose DECLARE statements contain 'the word "ALIAS". These cannot be used in the environment and 'the operation of the screen save and restore subs depend on 'them. If you *must* have this capability in the QB environment, 'I have SUBs that will accomplish these tasks without using 'the QB internal routines and will post them if it is requested, 'however they are quite a bit longer. 'Please note also that two of the DECLAREs have been broken in 'half because they were too long to fit on a single line. They 'will have to be restored before compiling. 'I would appreciate any comments or bug reports that anyone would 'be willing to share. Thanks. DECLARE SUB Set320x400mode () DECLARE SUB XCLS (Page%) DECLARE SUB ShowPage (Page%) DECLARE SUB PutPixel (X%, Y%, Culler%, Page%) DECLARE SUB GetPixel (X%, Y%, Culler%, Page%) DECLARE SUB XPRINT (X%, Y%, Text$, Culler%, Page%) DECLARE SUB XScrn2File (FileName$, Page%) DECLARE SUB XFile2Scrn (FileName$, Page%) DECLARE SUB QBPUT3 ALIAS "B$PUT3" (BYVAL FileNumber%, BYVAL Segment%, BYVAL Address%, BYVAL NumBytes%) DECLARE SUB QBGET3 ALIAS "B$GET3" (BYVAL FileNumber%, BYVAL Segment%, BYVAL Address%, BYVAL NumBytes%) DEFINT A-Z DIM BitMask%(7) FOR Bit% = 0 TO 7: BitMask%(Bit%) = 2 ^ Bit%: NEXT File0$ = "TEST0.S2F": File1$ = "TEST1.S2F" CALL Set320x400mode CALL XPRINT(40, 0, "Hello from PAGE 0", 4, 0) FOR Y% = 16 TO 399 C% = Y% \ 8 + 16 FOR X% = 0 TO 319 CALL PutPixel(X%, Y%, C%, 0) NEXT NEXT CALL XScrn2File(File0$, 0) CALL ShowPage(1) CALL XPRINT(50, 300, "Press any key.", 0, 0) CALL XPRINT(40, 0, "Hello from PAGE 1", 2, 1) FOR X% = 0 TO 319 C% = X% \ 8 + 16 FOR Y% = 16 TO 399 CALL PutPixel(X%, Y%, C%, 1) NEXT NEXT CALL XScrn2File(File1$, 1) CALL ShowPage(0) CALL XPRINT(50, 300, "Press any key.", 0, 1) SLEEP 2 DO UNTIL LEN(INKEY$) CALL ShowPage(1): SLEEP 2 CALL ShowPage(0): SLEEP 2 LOOP CALL ShowPage(0) DO CALL XCLS(0) CALL XPRINT(0,0,"Indicate which page to restore: (0/1) ",15,0) A$ = INPUT$(1) SELECT CASE A$ CASE "0": CALL XFile2Scrn(File0$, 0) CASE "1": CALL XFile2Scrn(File1$, 0) CASE CHR$(27): EXIT DO END SELECT CALL XPRINT(50, 280, "Press ESC to exit.", 0, 0) CALL XPRINT(50, 300, "Press any other key to continue.", 0, 0) A$ = INPUT$(1): IF A$ = CHR$(27) THEN EXIT DO LOOP SCREEN 0: WIDTH 80 END SUB GetPixel (X%, Y%, Culler%, Page%) 'this is code to read the color of a pixel in MODEX 320x400x256 'this code is based on ASM code written by Michael Abrash ' and published in 1989 Programmer's Journal 7.1 'by Douglas H. Lusher - June 15, 1996 Segment% = &HA000 IF Page% THEN Segment% = &HA800 OUT &H3CE, 4 'select the Read Map Select Register OUT &H3CF, X% AND 3 'indicate the bit plane desired DEF SEG = Segment% Culler% = PEEK((Y% * 80) + (X% \ 4)) END SUB SUB PutPixel (X%, Y%, Culler%, Page%) 'this is code to write a pixel in MODEX 320x400x256 'this code is based on ASM code written by Michael Abrash ' and published in 1989 Programmer's Journal 7.1 'by Douglas H. Lusher - June 15, 1996 SHARED BitMask%() Segment% = &HA000 IF Page% THEN Segment% = &HA800 OUT &H3C4, 2 'select the Map Mask Register OUT &H3C5, BitMask%(X% AND 3) 'indicate the desired bit plane DEF SEG = Segment% POKE (Y% * 80) + (X% \ 4), Culler% END SUB SUB Set320x400mode 'the following code sets the VGA to 320x400x256 mode 'this is a translation from ASM code written by Michael Abrash ' and published in 1989 Programmer's Journal 7.1 'by Douglas H. Lusher - June 15, 1996 'start with basic SCREEN 13 SCREEN 13 'change the CPU addressing of video memory to linear (not odd/even, 'chain, or chain 4) to allow access to all 256K of display memory. SCI% = &H3C4 'the Sequence Controller Index register MemoryMode% = 4 'memory mode register index OUT SCI%, MemoryMode% X% = INP(&H3C5) X% = X% AND NOT &H8 'turn off chain 4 X% = X% OR &H4 'turn off odd/even OUT &H3C5, X% GCI% = &H3CE 'the Graphics Controller Index register GraphicsMode% = 5 'graphics mode register index OUT GCI%, GraphicsMode% X% = INP(&H3CF) X% = X% AND NOT &H10 'turn off odd/even OUT &H3CF, X% Miscellaneous% = 6 'miscellaneous register index OUT GCI%, Miscellaneous% X% = INP(&H3CF) X% = X% AND NOT &H2 'turn off chain OUT &H3CF, X% 'clear all of video memory - setting mode 13 only cleared 64K 'of video memory. Do this before switching the crt out of mode 13 'so that garbage does not appear on the screen when the switch 'is made. MapMask% = 2 OUT SCI%, MapMask% OUT &H3C5, &HF DEF SEG = &HA000 'clear page 0 FOR Ptr% = 0 TO 31999: POKE Ptr%, 0: NEXT DEF SEG = &HA800 'clear page 1 FOR Ptr% = 0 TO 31999: POKE Ptr%, 0: NEXT 'tweak the mode to 320x400x256 by not scanning each line twice CRTCI% = &H3D4 'the CRT Controller Index register MaxScanLine% = 9 'maximum scan line register index OUT CRTCI%, MaxScanLine% X% = INP(&H3D5) X% = X% AND NOT &H1F 'set max scan line = 0 OUT &H3D5, X% 'change CRTC scanning from doubleword mode to byte mode, allowing 'the CRTC to scan more than 64K of video data Underline% = &H14 'underline location register index OUT CRTCI%, Underline% X% = INP(&H3D5) X% = X% AND NOT &H40 'turn off doubleword OUT &H3D5, X% ModeControl% = &H17 'mode control register index OUT CRTCI%, ModeControl% X% = INP(&H3D5) X% = X% OR &H40 'turn on the byte mode bit, so memory is 'video memory is scanned linearly, just as 'in modes &H10 and &H12 OUT &H3D5, X% END SUB SUB ShowPage (Page%) 'this code displays the specified page in MODEX - 320x400x256 'by Douglas H. Lusher - June 16, 1996 SELECT CASE Page% CASE 0 'display page 0 OUT &H3D4, &HC: OUT &H3D5, 0 CASE 1 'display page 1 OUT &H3D4, &HC: OUT &H3D5, &H80 CASE ELSE ERROR 5 'illegal function call END SELECT END SUB SUB XCLS (Page%) 'this is code to clear the screen in MODE X - 320x400x256 'by Douglas H. Lusher - June 16, 1996 SHARED BitMask%() 'turn off the screen while it is being erased OUT &H3C4, 1: CMR% = INP(&H3C5): OUT &H3C5, CMR% OR &H20 Segment% = &HA000: IF Page% THEN Segment% = &HA800 DEF SEG = Segment% OUT &H3C4, 2 'select the Map Mask Register FOR Plane% = 0 TO 3 OUT &H3C5, BitMask%(Plane%) 'indicate the desired bit plane FOR Address% = 0 TO 31999: POKE Address%, 0: NEXT NEXT 'turn the screen back on... OUT &H3C4, 1: CMR% = INP(&H3C5): OUT &H3C5, CMR% AND &HDF END SUB SUB XFile2Scrn (FileName$, Page%) 'this code loads a screen image (including the palette) from ' the specified file and displays it on the specified screen ' page in MODEX - 320x400x256 'NOTE: this CANNOT be run in the QB45 environment, it must be ' compiled and this DECLARE statement must appear in the code: 'DECLARE SUB QBGET3 ALIAS "B$GET3" (BYVAL FileNumber%, BYVAL Segment%, BYVAL Address%, BYVAL NumBytes%) 'by Douglas H. Lusher - June 16, 1996 SHARED BitMask%() Bytes% = 32000 '(320 * 400) \ 4 ColorRegs% = 768 'the number of colors * 3 Segment% = &HA000 IF Page% THEN Segment% = &HA800 OUT &H3C8, 0 'blank the screen FOR X% = 1 TO ColorRegs%: OUT &H3C9, 0: NEXT File% = FREEFILE OPEN FileName$ FOR BINARY AS File% FOR X% = 0 TO 3 OUT &H3C4, 2 'select the Map Mask Register OUT &H3C5, BitMask%(X%) 'indicate the bit plane to restore CALL QBGET3(File%, Segment%, 0, Bytes%) NEXT P$ = SPACE$(ColorRegs%) Segment% = VARSEG(P$): Offset% = SADD(P$) CALL QBGET3(File%, Segment%, Offset%, ColorRegs%) CLOSE File% DEF SEG = Segment% OUT &H3C8, 0 FOR X% = Offset% TO Offset% + ColorRegs% - 1 OUT &H3C9, PEEK(X%) NEXT END SUB SUB XPRINT (X%, Y%, Text$, Culler%, Page%) 'this is code to print text in MODEX - 320x400x256 'by Douglas H. Lusher - June 15, 1996 SHARED BitMask%() ' 8 x 8 char box, CGA 'CharSegment% = &HFFA6: CharOffset% = &HE 'CharWid% = 8: CharHgt% = 8 ' 8 x 14 char box, EGA CharSegment% = &HC000: CharOffset% = &H4ED5 CharWid% = 8: CharHgt% = 14 ' 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 'CharWid% = 8: CharHgt% = 16 XX% = X% - 1 FOR Char% = 1 TO LEN(Text$) XX% = XX% + CharWid% YY% = Y% Ptr% = CharHgt% * ASC(MID$(Text$, Char%, 1)) + CharOffset% FOR Ln% = 0 TO CharHgt% - 1 DEF SEG = CharSegment% BitPattern% = PEEK(Ptr% + Ln%) FOR Bit% = 0 TO 7 IF BitPattern% AND BitMask%(Bit%) THEN CALL PutPixel(XX% - Bit%, YY%, Culler%, Page%) END IF NEXT YY% = YY% + 1 NEXT NEXT END SUB SUB XScrn2File (FileName$, Page%) 'this code saves a screen image (including the palette) from ' the specified screen page and saves it to the specified file ' in MODEX - 320x400x256 'NOTE: this CANNOT be run in the QB45 environment, it must be ' compiled and this DECLARE statement must appear in the code: 'DECLARE SUB QBPUT3 ALIAS "B$PUT3" (BYVAL FileNumber%, BYVAL Segment%, BYVAL Address%, BYVAL NumBytes%) 'by Douglas H. Lusher - June 16, 1996 Bytes% = 32000 '(320 * 400) \ 4 = 30,000 bytes ColorRegs% = 768 'the number of colors * 3 Segment% = &HA000 IF Page% THEN Segment% = &HA800 File% = FREEFILE OPEN FileName$ FOR BINARY AS File% FOR X% = 0 TO 3 OUT &H3CE, 4 'select the Read Map Select Register OUT &H3CF, X% 'indicate the bit plane to save CALL QBPUT3(File%, Segment%, 0, Bytes%) NEXT P$ = SPACE$(ColorRegs%) Segment% = VARSEG(P$): Offset% = SADD(P$) DEF SEG = Segment% OUT &H3C7, 0 FOR X% = Offset% TO Offset% + ColorRegs% - 1 POKE X%, INP(&H3C9) NEXT CALL QBPUT3(File%, Segment%, Offset%, ColorRegs%) CLOSE File% END SUB