                          Advanced SVGA Part 2
                            by Anthony Tyler 

Introduction

        Hi everybody, and welcome back to part 2 of "Advanced SVGA". First
of all I recommend, that you read part 1 of this series, before you read this,
because this builds on the knowledge we acquired in part 1. This time, we will
learn how to write pixels to the screen in a 640x480 screen mode. Okay, enough
of the introduction! Let's start programming!

Theory

        The SVGA, like the VGA, is written to by the video segment, &HA000. So
to write pixels to a SVGA screen we must simple write the value to the proper
location in &HA000. This is rather simple, but we do arrive at one problem.
SVGA screen modes require more than the measly 64k that the video bios
requires us to use, so VESA made function 05h(remember all VESA function are
prefixed with a 4Fh). This function changes the bank (read Aaron Severn's
documents for more info on banks). The bank is basically a window used to
access video memory. To write to video memory we must first find the bank
in which to write to, we then must find where in that 64k to write a pixel
to.

Writing Pixels

        First of all, let's find which screen mode we want to use. Let's use
112h. This is a 640x480 screen mode, with 16,777,216 colors. This is a 24 bit
screen mode(or 1 byte per attribute). This makes it quite easy to write pixels
all we have to do is write the RGB values to the proper offsets. Let me show 
you how to write pixels, and then we will step through it one line at a time.

SUB Pixel (X%, Y%, R%, G%, B%)
offset& = (Y% * 4) * 640& + (X% * 4)
BANK% = INT(offset& \ 65536)
offset& = offset& - 65536 * BANK%
IF BANK% <> curbank% THEN
  Regs.ax = &H4F05
  Regs.bx = 0
  Regs.dx = curBank
  CALL INTERRUPTX(&H10, InRegs, OutRegs)
END IF
curbank% = BANK%
DEF SEG = &HA000
POKE offset&, b%
POKE offset& + 1, g%
POKE offset& + 2, r%
END SUB

Let us begin to analyze this code. The first line is:

offset& = (Y% * 4) * 640& + (X% * 4)

        This is the most important part, of the sub. What this does is it
calculates where we should put the pixel. The reason we multiply the X and Y
values by 4 is because each pixel takes four bytes(red, green, blue, and
reserved). Reserved can be any value, and it does not effect the pixel. We
multiply the Y * 4 by 640 because that is the width of X, video memory is
stored the same in SVGA modes as in VGA modes:

        0   1   2   3   4   5   6   7   8   9
        640 641 642 643 644 645 646 647 648 649

We then just add the X value(times 4). The next piece of code is:

BANK% = INT(offset& \ 65536)

        This calculates our bank. Since screen modes can only be accessed 64k
at a time(65536 bytes). We must divide our offset by 64k to find the right
bank. This is rather simple to understand. 

offset& = offset& - 65536 * BANK%

        This piece of code adjusts our offset to be within the 64k boundaries.
Since we found the right bank. We then must subtract the bank * 65536 by our
offset. For example, when we calculate our offset we get 65537. We find that
our bank is bank 1. We then must find where in this bank, is our pixel. We do
this calculation offset& = offset& - 65536 * BANK%. And our result is 1, so
our pixel is at offset 1 in the first bank. Our next piece of code, is the
bank setting code.

IF BANK% <> curbank% THEN
  Regs.ax = &H4F05
  Regs.bx = 0
  Regs.dx = curBank
  CALL INTERRUPTX(&H10, InRegs, OutRegs)
  curbank% = BANK%
END IF

        This code saves us quite a bit of time. What it does is checks to see
if the BANK our pixel is at is the BANK we are already at. If not, we set
the BANK and change the variable curbank% to equal our current BANK, if so we
do not need to waste our time setting a bank that we are already at. 

DEF SEG = &HA000
POKE offset&, b%
POKE offset& + 1, g%
POKE offset& + 2, r%

        The final piece of code, is the code that actually sets the pixel. All
we do is set the segment to the video segment, and POKE the RGB values in
reverse order. We do this because that is the way VESA made it. 

Optimizations

        Now is the fun part. Our current pixel routines, are really slooooow.
We cannot use these routines anywhere, because they are so slow. What we need
to do is optimize them. We do this by writing the code in assembly. Since
most of you probably don't have an assembler I will post the library at the
end of this document. Here's the assembler code for the pixel routine:

                bank dw ?
                PUBLIC Pixel
Pixel           PROC FAR X:WORD,Y:WORD,R:WORD,G:WORD,B:WORD
                MOV AX,0A000h
                MOV ES,AX
                MOV AX,Y
                SHL AX,2
                MOV BX,AX
                SHL EAX,9
                SHL EBX,7
                ADD EAX,EBX
                XOR EBX,EBX
                MOV BX,X
                SHL BX,2
                ADD EAX,EBX
                MOV EBX,EAX
                SHR EBX,16
                MOV ECX,EBX
                SHL ECX,16
                SUB EAX,ECX
                CMP BX,bank
                JNE bankset
pixelset:       MOV DI,AX
                MOV AX,B
                STOSB
                MOV AX,G
                STOSB
                MOV AX,R
                STOSB
                RET
bankset:        PUSH AX
                MOV DX,BX
                MOV AX,4F05h
                XOR BX,BX  
                INT 10h    
                POP AX
                MOV bank,DX
                JMP pixelset
ENDP            PIXEL

        Without a great knowledge of assembly language this code would be very
hard to decipher, therefore I will not step through this code line by line.
Optimizing it in assembly language made the procedure 20 times faster. Well,
since this document is getting pretty large, I guess I will wrap up this part.
The next part of this series will be on more pixel drawing, and maybe line
and circle drawing. Oh, and before I forget get our current library here. Have
fun! Happy programming!!

