QBXL Technical Articles

VGA Boot Camp

 Hello there maggot, and welcome to video mode Bootcamp. This will NOT be a walk in the park. This will NOT be fun. You will likely CRY. LIKE A BABY. If you're the sort who can't handle the sight of blood or the thought of low level programming, THIS TUTORIAL IS NOT FOR YOU.

Video memory

Now, since you are the sorryest bunch of apes I've ever had the misfortune to have to deal with, I'll start with something easy -- video memory. It's so easy that my poor sweet momma in the nursing home can understand it, so LISTEN UP. I'm only going to explain this once, so you'd BETTER pay attention!

There are generally THREE ways that video memory can be set up. These are PLANAR, NON-PLANAR, and THAT CREEPY THING REAL-MODE VESA DOES. Those are all SCIENTIFIC terms. REMEMBER THEM. They WILL save your life.

Now PLANAR modes are unique to ModeX. These are also known as NON-CHAINED modes. In ModeX, they're generally set up in 4 planes, which are set up like this:



0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3
0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3
0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3


In order to write to a pixel on a certain plane, you have to tell the video card which you plan to write to. Luckily for us, you DO NOT need to do this every time you write to memory, just when you switch planes. This means that with care, most ModeX video modes are capable of running nearly as fast as a regular video mode. We'll come back to this later.

NON PLANAR MODES, also called CHAINED modes, are set up so all the memory can be seen in one continuous block. Any pixel on the screen can be accessed at any time. Mode 13h, 320x200 uses this scheme. It's phenomally fast to acces because no extra work is needed. However, it's quite memory inefficient, hence the 320x200 limit.

THAT CREEPY THING REAL-MODE VESA DOES is creepy. Every 4, 16, 32, or 64k based on the GRANULARITY setting in the video card, you have to tell the video card to head to the next memory segment, so the memory at 0xa000(we'll get to that in a second) becomes the memory from the 1st pixel to the 4 thousandth, from the 4 thousandth to the 8 thousandth, and so on. It's definitely creepy. Something a standard maker would think up. The worst part is that it's horribly complicated trying to optimze a standard pset routine as a result, because you never really know when you're going to have to draw a sprite over an interection of two territories, and incur the ultimate speed penalty of a bunch of segment changes(and trying to detect said segment changes) without careful planning! THIS IS WHY VESA MODES IN REAL-MODE DOS IS SLOW, PEOPLE!

Setting up video modes

There are THREE ways that you will be able to set up video modes. These are CALLING THROUGH THE INTERRUPT 10h, SETTING THE VGA REGISTERS DIRECTLY, and THAT CREEPY THING REAL-MODE VESA DOES.

CALLING THROUGH INTERRUPT 10H is the easiest way. In QB, you just use the SCREEN statement. When you say SCREEN 13, you're saying "Set ax to and call interrupt 10h". I can't explain this any better, because it's too easy.

SETTING THE VGA REGISTERS DIRECTLY is how we get MODE X and MODE Q modes. Setting these is quite DANGEROUS. You WILL NOT do this on your own. Only use either sample code from others or technical specs from others. You CAN and likely WILL damage your hardware if you play with this incorrectly!

THAT CREEPING THING REAL-MODE VESA DOES isn't really that creepy. It's just calling another interrupt. IT'S STILL CREEPY THOUGH, because IT IS REAL MODE VESA. Remember that. REAL MODE VESA IS CREEPY.



Examples

Ever since that PANSY LIBERAL PINKO of a judge from LEFT WING TEXAS said that we COULDN'T put troops in the field and ask them to program hardware without showing them HOW, we now have to give four examples of graphics programming. Remember to PAY ATTENTION, because this information IS VITAL. ESPECIALLY if you're a weak little mommas boy who couldn't figure out how to do this from the paragraphs ABOVE.

FIRST OFF, we will show you an example of simple Mode 13h, which is 320x200. It uses CALLING THROUGH INTERRUPT 10H to set it's video mode, and is a NON PLANAR MODE.



declare sub putd(x%, y%, colr%)
def seg = &Ha000

screen 13

FOR a = 0 TO 255
FOR b = 0 TO 255
putd a, b, c
NEXT b
NEXT a

STATIC SUB putd (x, y, colr)
POKE (x * 320&) + y, colr
END SUB


SECOND, we will show you an example of Mode Q. Mode Q is 256x256. It uses SETTING THE VGA REGISTERS DIRECTLY to set up the screen, and is a NON PLANAR MODE. PAY ATTENTION to the setModeQ routine, which sets up the video mode, and the putd routine, which draws pixels to the screen.



DEFINT A-Z
def seg = &Ha000

DECLARE SUB setModeQ ()
DECLARE SUB deinit ()
DECLARE SUB putd (x%, y%, colr%)

setModeQ
WHILE INKEY$ = ""


c = INT(RND(1) * 255)

FOR a = 0 TO 255
FOR b = 0 TO 255
putd a, b, c
NEXT b
NEXT a

WEND

deinit

SUB deinit ()
CLS
SCREEN 13: SCREEN 0: WIDTH 80
END

END SUB

STATIC SUB putd (x, y, colr)

POKE (x * 256&) + y, colr

END SUB

DEFSNG A-Z
SUB setModeQ ()
DEF SEG = &HA000
'begin with standard 320x200x256 mode
SCREEN 13

'to reprogram the CRT controller,
'remove write protect from the registers
OUT &H3D4, &H11: OUT &H3D5, INP(&H3D5) AND &H7F


OUT &H3D4, &H0: OUT &H3D5, &H5F 'Hor. Total
OUT &H3D4, &H1: OUT &H3D5, &H3F 'Hor. Display enable end
OUT &H3D4, &H2: OUT &H3D5, &H40 'blank start
OUT &H3D4, &H3: OUT &H3D5, &H82 'blank end
OUT &H3D4, &H4: OUT &H3D5, &H4E 'retrace start
OUT &H3D4, &H5: OUT &H3D5, &H9A 'retrace end
OUT &H3D4, &H6: OUT &H3D5, &H23 'vertical total
OUT &H3D4, &H7: OUT &H3D5, &HB2 'overflow register
OUT &H3D4, &H8: OUT &H3D5, &H0 'preset row scan
OUT &H3D4, &H9: OUT &H3D5, &H61 'max scan line/char height
OUT &H3D4, &H10: OUT &H3D5, &HA 'vertical retrace start
OUT &H3D4, &H11: OUT &H3D5, &HAC 'vertical retrace end
OUT &H3D4, &H12: OUT &H3D5, &HFF 'vertical display enable end
OUT &H3D4, &H13: OUT &H3D5, &H20 'offset/logical width
OUT &H3D4, &H14: OUT &H3D5, &H40 'underlinde location
OUT &H3D4, &H15: OUT &H3D5, &H7 'vertical blank start
OUT &H3D4, &H16: OUT &H3D5, &H17 'vertical blank end
OUT &H3D4, &H17: OUT &H3D5, &HA3 'mode control

OUT &H3C4, &H1: OUT &H3C5, &H1 'clock mode register
OUT &H3C4, &H4: OUT &H3C5, &HE 'memory mode register

OUT &H3CE, &H5: OUT &H3CF, &H40 'mode register
OUT &H3CE, &H6: OUT &H3CF, &H5 'misc. register

OUT &H3C0, &H10 + 32: OUT &H3C1, &H41 'mode control

'reprotect the registers
OUT &H3D4, &H11: OUT &H3D5, INP(&H3D5) OR &H80


END SUB

THIRDLY, we have here a FINE EXAMPLE of MODE X, at 320x240. THIS IS A PLANAR MODE. It SETS THE VGA REGISTERS DIRECTLY, and is one of the HARDEST video modes to use correctly. PAY ATTENTION to every part of this complicated routine, and you MAY make it out there, you WORTHLESS PIECE OF DIRT!



DECLARE SUB putd (sx%, sy%, colr%)
DECLARE SUB pbox (firstx%, lastx%, firsty%, lasty%, colr%)
DECLARE SUB pageflip ()
DECLARE SUB box (firstx%, lastx%, firsty%, lasty%, colr%)
DECLARE SUB xcls (page%)
DECLARE SUB showpage (page%)
DECLARE SUB Set320x240mode ()

DEFINT A-Z
def seg = &Ha000

DIM SHARED page AS INTEGER
DIM bitmask%(7)
FOR Bit% = 0 TO 7: bitmask%(Bit%) = 2 ^ Bit%: NEXT

PRINT "Sj Zeros Optimization demonstration."
PRINT
PRINT "Press any key to start"
SLEEP

Set320x240mode
'The XCLS is needed or else the program will crash.
xcls 0
xcls 1
xcls 2

'Program loop goes here!
'meant to set the video page and keep it there.
'Pageflip defs the seg, and then I show that video page. It looks awful
'if you triple buffer the below demonstrations...
pageflip
showpage 1

FOR v = 1 TO 5
FOR a = 0 TO 255
box 0 + a, 320 - a, 0 + a, 240 - a, a'This fills the entire screen with new pixels.
'Just meant to demonstrate the speed of this.
NEXT a
NEXT v

SCREEN 13: SCREEN 0: WIDTH 80
PRINT "That was the fast algorithm in action. Press space to see the slow"
PRINT "algorithm do the same thing to see the speed difference."
SLEEP

Set320x240mode

'The XCLS is needed or else the program will crash.
xcls 0
xcls 1
xcls 2


'Program loop goes here!

'This is meant to set the video page and keep it there.
pageflip
showpage 2

FOR v = 1 TO 5
FOR a = 0 TO 255
pbox 0 + a, 320 - a, 0 + a, 240 - a, a'This fills the entire screen
with new pixels.
'Just meant to demonstrate the speed of this.
NEXT a
NEXT v
PRINT "Quite a difference, eh?"
PRINT "Enjoy!"
SCREEN 13: SCREEN 0: WIDTH 80
END SUB box (firstx%, lastx%, firsty%, lasty%, colr%)
'This is probably the fastest way to write to a modex screen.
'in my own game I replaced the a%/4 with a function that returned
'the result of that operation. a% is between 0 and 320, but while
'that is a lot of memory, it's worth the speed increase in the end.
SHARED bitmask%()
startedhere% = firsty% * 80 'this sets the vertical position.
FOR a% = (firstx% - 1) TO lastx%
OUT &H3C5, bitmask%(a% AND 3) 'Sets the bitplane *once per line*
'very important for speed.
sy% = a% \ 4 + startedhere% 'This sets the horizontal position
FOR b% = firsty% TO lasty% 'moves vertically
sy% = sy% + 80 'adds 80 to the video pointer
POKE sy%, colr% 'places that pixel!
'code to read a screen buffer could easily be placed here
'to use this in any kind of game, even a crappy
'pure QB 3d shooter! :)
NEXT b%, a%

END SUB

SUB pageflip
'This is a nice routine I wrote that didn't come with the original.
'This will change the video focus while rendering. important for
'games that take advantage of triple buffering.

showpage page
IF page = 1 THEN page = 2 ELSE IF page = 2 THEN page = 0 ELSE IF page = 0

THEN page = 1

SELECT CASE page 'This *was* in the putpixel routine. Evil.
CASE 0: VidSegment% = &HA000
CASE 1: VidSegment% = &HA4F0
CASE 2: VidSegment% = &HA9E0
CASE ELSE: ERROR 5
END SELECT
DEF SEG = VidSegment%

END SUB

SUB pbox (firstx%, lastx%, firsty%, lasty%, colr%)

FOR x% = firstx% TO lastx%
FOR y% = firsty% TO lasty%
putd x%, y%, colr%
NEXT y%
NEXT x%


END SUB
SUB putd (sx%, sy%, colr%) STATIC
'Clipping is done, despite the speed loss.
IF colr% <> 0 AND sx% > -1 AND sx% < 320 AND sy% < 240 THEN
SHARED bitmask%()
OUT &H3C5, bitmask%(sx% AND 3) 'This must set the plane......
POKE sy% * 80 + sx% \ 4, colr%
END IF
END SUB
SUB Set320x240mode
'begin with standard 320x200x256 mode
SCREEN 13
'disable "chain4" mode
OUT &H3C4, &H4: OUT &H3C5, &H6
'enable writes to all four planes
OUT &H3C4, &H2: OUT &H3C5, &HF
'clear video memory

'synchronous reset while switching clocks
OUT &H3C4, 0: OUT &H3C5, &H1
'select 25 Mhz dot clock and 60 hz scanning rate
OUT &H3C2, &HE3
'restart the sequencer
OUT &H3C4, 0: OUT &H3C5, &H3
'to reprogram the CRT controller,
'remove write protect from the registers
OUT &H3D4, &H11: OUT &H3D5, INP(&H3D5) AND &H7F
OUT &H3D4, &H6: OUT &H3D5, &HD 'total vertical pixels
OUT &H3D4, &H7: OUT &H3D5, &H3E 'overflow
OUT &H3D4, &H9: OUT &H3D5, &H41 'turn off double double-scan
OUT &H3D4, &H10: OUT &H3D5, &HEA 'vertical sync start
OUT &H3D4, &H11: OUT &H3D5, &HAC 'vertical sync end, reprotect registers
OUT &H3D4, &H12: OUT &H3D5, &HDF 'vertical pixels displayed
OUT &H3D4, &H14: OUT &H3D5, 0 'turn off dword mode
OUT &H3D4, &H15: OUT &H3D5, &HE7 'vertical blank start
OUT &H3D4, &H16: OUT &H3D5, &H6 'vertical blank end
OUT &H3D4, &H17: OUT &H3D5, &HE3 'turn on byte mode
OUT &H3C4, 2 'I removed this from the main putpixel loop.
'It used to be in the main renderer, but it's slow, and only
'needed once, at execution.
END SUB

SUB showpage (page%)
SELECT CASE page%
CASE 0: OUT &H3D4, &HC: OUT &H3D5, 0
CASE 1: OUT &H3D4, &HC: OUT &H3D5, &H4F
CASE 2: OUT &H3D4, &HC: OUT &H3D5, &H9E
CASE ELSE: ERROR 5 'illegal function call
END SELECT
END SUB

SUB xcls (page%)
SELECT CASE page%
CASE 0: VidSegment% = &HA000
CASE 1: VidSegment% = &HA4F0
CASE 2: VidSegment% = &HA9E0
CASE ELSE: ERROR 5
END SELECT
OUT &H3C4, &H2: OUT &H3C5, &HF
DEF SEG = VidSegment%
FOR Address% = 0 TO 19199: POKE Address%, 0: NEXT
END SUB

I HOPE YOU GOT THAT, BOY! This is EXTREMELY COMPLICATED STUFF! You wanna GO HOME? wanna go home and CRY to MOMMA? DO YOU? Tough. This is the fighting mans army, so you gotta stay and fight like the rest of us.

FINALLY. We have an example of REAL-MODE VESA. As you recall, this is CREEPY. You'd better learn it fast though, or you'll be GOING DOWN FASTER THAN AN INTERN IN THE WHITEHOUSE.


'$INCLUDE: 'QB.BI'
DEF SEG = &HA000
set640x480mode
FOR x% = firstx% TO lastx%
FOR y% = firsty% TO lasty%
putd x%, y%, colr%
NEXT y%
NEXT x%

END


DEFINT A-Z
SUB putd (x, y, clr)
offset& = x + xRes * y

Bank% = offset& \ 65536
offset& = offset& + Bank% * 65536


IF Bank <> Banknow THEN
Banknow = Bank%
regs.ax = &H4F05
regs.bx = 0
regs.dx = Bank%
CALL INTERRUPT(&H10, regs, regs)
END IF POKE offset&, clr
END SUB


END SUB

END SUB
SUB set640x480mode
xRes = 640
regs.ax = &H4F02
regs.bx = &H101
CALL INTERRUPT(&H10, regs, regs)

END SUB


THERE. I've done ALL I CAN DO. You'd better PRAY TO GOD that you got that.
Good luck, I think I've taught you well. Go out there and make us all proud, You bunch of apes!! MOVE IT!

-

--SJ Zero won't be writing many more QB articles, so eat up! FB does all this stuff on it's own!