                             B I O S   C a l l s

                          i n   Q u i c k B a s i c

                         -A Programmer's Best Friend-








                           Written by Aaron Severn

                              December 23, 1997




-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

Table of Contents
-----------------

0 Disclaimer
1 Introduction
2 How to Perform BIOS Calls in QuickBasic
3 A Few Examples
  3.1 Mouse
  3.2 SVGA
  3.3 EMS

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

0 Disclaimer
------------

  I assume no responsibility for any harm that comes from using the material
contained in this document to you, your computer, or anything relating to
your existence.  No warranty is provided or implied with this information.
This document is provided as is.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

1 Introduction
--------------

  There's a lot of things I'm sure you'd like to do in your programs, but
you likely just don't know where to start.  Just to use the mouse you'd
have to write up a huge amount of code to read and interpret data from the
mouse port, or would you?  Actually, that code has already been written,
it's sitting in your computer's memory waiting for you to use it.  So how
do you use it?  BIOS calls.

  QuickBasic has a very handy command called CALL INTERRUPT (also CALL
INTERRUPTX, I'll explain the difference later) which allows you to perform
all the BIOS calls you could ever want.  You can do just about anything
with BIOS calls, even draw graphics (although you wouldn't want to, they're
kind of slow for that purpose).  So now I'll explain how.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

2 How to Perform BIOS Calls in QuickBasic
-----------------------------------------

  Before you can begin, you need a set of registers.  That's what you use
to tell BIOS exactly what you want to do.  First, start by defining a
variable type in your program that looks like this.

  TYPE RegType
      AX AS INTEGER
      BX AS INTEGER
      CX AS INTEGER
      DX AS INTEGER
      BP AS INTEGER
      SI AS INTEGER
      DI AS INTEGER
      FLAGS AS INTEGER
      DS AS INTEGER
      ES AS INTEGER
  END TYPE

  Those familiar with assembly will immediately recognize this and know what
to do with it.  To the rest of you, don't worry, you really don't have to
know what it means, just use it.

  Once you've got that, you'll have to DIM an array of that type which will
be used in your CALL INTERRUPT statements.  It should look something like
this:

  DIM Regs AS RegType

  Now all you have to do is pop in the right values for the registers and pop
that into CALL INTERRUPT.  Here's a very simple example, see below for better
examples.  Here's what it does, first it changes to screen mode 13 using a
BIOS call, then it fills the screen (using POKE, PSET won't work because
QBasic thinks we're still in text mode), then it changes back to text mode
using another BIOS call.

  DEFINT A-Z

  TYPE RegType                        'Define the registers
      AX AS INTEGER
      BX AS INTEGER
      CX AS INTEGER
      DX AS INTEGER
      BP AS INTEGER
      SI AS INTEGER
      DI AS INTEGER
      FLAGS AS INTEGER
      DS AS INTEGER
      ES AS INTEGER
  END TYPE

  DIM Regs AS RegType                 'Create an array of registers

  Regs.AX = &H13                      'BIOS call: interrupt &H10, AX = &H13
  CALL INTERRUPT(&H10, Regs, Regs)    'changes to mode 13

  DEF SEG = &HA000                    'Point to the graphics buffer
  FOR i& = 0 to 63999
     POKE i&, i& MOD 256              'Fill the screen
  NEXT

  DO: LOOP UNTIL INKEY$ <> ""         'Wait for a key

  Regs.AX = &H3                       'BIOS call: interrupt &H10, AX = &H3
  CALL INTERRUPT(&H10, Regs, Regs)    'changes to text mode

  So that example isn't very useful, check out the ones below, you'll see
where it becomes handy.  Still, that pretty nice, isn't it.

  Anyway, I said I'd explain the difference between CALL INTERRUPT and CALL
INTERRUPTX, so I'll do that now.  CALL INTERRUPT ignores the DS and ES
registers, CALL INTERRUPTX uses those registers (as well as all the other
ones).  So if you need to perform a BIOS call that uses the DS and ES
registers, use CALL INTERRUPTX.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

3 A Few Examples
----------------

  NOTE:  In all examples below it is assumed that the type RegType described
in section 2 has been defined and that a variable Regs has been DIMmed of
that type.

3.1 Mouse
---------

  Most of you probably use a mouse routine that has been written in ASM for
CALL ABSOLUTE.  Well, you can do it with CALL INTERRUPT too, here's a few
mouse routines.

  1. Reset the mouse to it's default position

    Regs.AX = 0
    CALL INTERRUPT(&H33, Regs, Regs)

  2. Show the mouse

    Regs.AX = 1
    CALL INTERRUPT(&H33, Regs, Regs)

  3. Hide the mouse

    Regs.AX = 2
    CALL INTERRUPT(&H33, Regs, Regs)

  4. Get the status of the mouse

    Regs.AX = 3
    CALL INTERRUPT(&H33, Regs, Regs)

    leftMouseButton = Regs.BX AND 1             'These values are
    rightMouseButton = (Regs.BX AND 2) \ 2      'placed in Regs by BIOS
    middleMouseButton = (Regs.BX AND 4) \ 4     'when you perform the
    xPosition = Regs.DX                         'call.
    yPosition = Regs.CX

  5. Move the mouse to a new place on the screen

    Regs.AX = 4
    Regs.CX = newYPosition
    Regs.DX = newXPosition
    CALL INTERRUPT(&H33, Regs, Regs)

  6. Define a range on the screen that the mouse 'lives' in

    Regs.AX = 7
    Regs.CX = minimumXValue
    Regs.DX = maximumXValue
    CALL INTERRUPT(&H33, Regs, Regs)

    Regs.AX = 8
    Regs.CX = minimumYValue
    Regs.DX = maximumYValue
    CALL INTERRUPT(&H33, Regs, Regs)

  7. Set the sensitivity of the mouse (how fast it moves)

    Regs.AX = 15
    Regs.CX = xSpeed                'Mouse moves faster as these values
    Regs.DX = ySpeed                'are decreased.
    CALL INTERRUPT(&H33, Regs, Regs)


3.2 SVGA
--------

  Yes, this too can be done with BIOS calls.  This will only be a short
example showing how to set a VESA SVGA screen mode.

  Regs.AX = &H4F02
  Regs.BX = SVGAMode
  CALL INTERRUPT(&H10, Regs, Regs)

  Here's a little SVGA mode info, put the following values in for SVGAMode:

    &H6A(106)
    &H100 - 640x400x256   = 255
    &H101 - 640x480x256   = 257 - packed pixel
    &H102 -  800x600x16    =  258 - 4-plane planar
    &H103 - 800x600x256   = 259 - packed pixel
    &H104 - 1024x768x16  = 260 -  4-plane planar
    &H105 - 1024x768x256  = 261
    &H107 - 1280x1024x256 = 263

  Just so you can see that it works I'll give you an SVGA PSET routine that
I wrote.  This routine assumes that a variable called curBank is global
(defined by COMMON SHARED or DIM SHARED) and is available to the routine.
Also, make sure you make the xResolution a long integer so that the math
will work (put an & on the end of it, for example, if xResolution is 640,
use 640&).

  offset& = y * xResolution& + x
  bank = offset& \ &H10000
  offset& = offset& - &H10000 * bank

  IF bank <> curBank THEN
    curBank = bank
    Regs.AX = &H4F05
    Regs.BX = 0
    Regs.DX = 16 * curBank
    CALL INTERRUPT(&H10, Regs, Regs)
  END IF

  DEF SEG = &HA000
  POKE offset&, clr

3.3 EMS
-------

  Just a quick examples to return how much EMS memory is available.

    Regs.AX = &H4200
    CALL INTERRUPT(&H67, Regs, Regs)

    numberOfFreePages = Regs.BX
    totalNumberOfPages = Regs.DX

  This, of course, doesn't really make use of EMS, so it's kind of useless,
but I've been writing for over an hour now, so I'm just going to leave it at
that.

