Copy Link
Add to Bookmark
Report

QBNews Volume 1 Number 3

eZine's profile picture
Published in 
QBNews
 · 28 Dec 2019

  

Volume 1, Number 3 May 22, 1990












**************************************************
* *
* QBNews *
* *
* International QuickBASIC Electronic *
* Newsleter *
* *
* Dedicated to promoting QuickBASIC around *
* the world *
* *
**************************************************
















The QBNews is an electronic newsletter published by Clearware
Computing. It can be freely distributed providing NO CHARGE is charged
for distribution. The QBNews is copyrighted in full by Clearware
Computing. The authors hold the copyright to their individual
articles. All program code appearing in QBNews is released into the
public domain. You may do what you wish with the code except
copyright it. QBNews must be distributed whole and unmodified.

You can write The QBNews at:

The QBNews
P.O. Box 507
Sandy Hook, CT 06482

Copyright (c) 1989 by Clearware Computing.



The QBNews Page i
Volume 1, Number 3 May 22, 1990




----------------------------------------------------------------------

T A B L E O F C O N T E N T S


1. From the Editors Desk
From the Editor .............................................. 1

2. Mail Bag
QBNews in Europe ............................................. 2

3. Beginners Corner
BASIC Menuing and Graphics by Ryan Snodgrass ................. 4

4. Who ya gonna call? CALL INTERRUPT
Directory Swapping by Hector Plasmic ......................... 6

5. The Tool Shed
P-Screen+ and P-Screen Professional by David Cleary .......... 8
Index Manager - B-Tree indexing for QB by David Cleary ....... 10

6. Product Announcements
P-Screen, P-Screen Professional, P~F Presents ................ 12

7. Under The Hood
Fast File I/O in QuickBASIC by Ethan Winer ................... 16

8. Power Programming
How to Make a Self-Cloning Exe in QuickBASIC by Larry Stone .. 19
Programming UEVENT by Jim Mack ............................... 23

9. Some Assembly Required
Assembler Programming for QuickBASIC by Tom Hanlin ........... 26

10. And I Heard it Through the Grapevine
Exerpts from the QUIK_BAS echo ............................... 29

11. Swap Shop
Screen Scrolling with Call Interrupt ........................ 33
Getting the Day of the Week with Call Interrupt ............. 35
Yes/No Response DEF FN ...................................... 37
A Replacement for INPUT with MUCH MORE programmer control. .. 39
Windowing Routines with Shading ............................. 41

12. Input Past End
Get the QBNews on Disk ...................................... 42
Contacting the QBNews ....................................... 43

The QBNews Page ii
Volume 1, Number 3 May 22, 1990



----------------------------------------------------------------------
F r o m t h e E d i t o r s D e s k
----------------------------------------------------------------------

From the Editor

Welcome to the third issue of The QBNews. I know it has been a
while coming but I hope it was worth the wait. There are some big
changes in store for the news. I have realized that putting out an
issue every 2 months is impractical. Therefore, The QBNews will be
published quarterly. Expect issues around these dates:

May 30
August 30
November 30
February 28

I have also received alot of requests for the news on disk. You
can now receive the QBNews on disk. Information on this service is
available in the back of this issue.

The QBNews is distributed through SDS on Fidonet. It seems that
SDS is not as far reaching as I had thought. In order that everybody
knows that the can get the news reasonably close to them, I would like
to set up distribution hubs around the country. I am looking for BBS's
in these areas who would like to be QBNews hubs: Baltimore/Washington,
Charlotte NC, Atlanta, Orlando, Memphis, New Orleans, Chicago,
Cincinnati, Minneapolis, Kansas City MO, Dallas, Denver, Phoenix, Los
Angeles, San Francisco, and Portland. I do require a few things in
order to be a hub. First, first time callers must be granted limited
download privledges. Second, you must accept file requests from Point
systems. Last, you must be willing to send the news down the line to
the next hub. If you meet these conditions and are interested in being
a hub, write me.

I want to hear from you. After the first issue, I received quite
a bit of feedback. I wasn't too happy with that issue. I thought the
second issue was really good. I didn't receive as much feedback on it
as the first though. It was kind of disappointing. Let me hear your
complaints, suggestions, coding tips, or whatever. It always helps to
know people are interested.

Thanks and enjoy.
David Cleary











The QBNews Page 1
Volume 1, Number 3 May 22, 1990



----------------------------------------------------------------------
M a i l B a g
----------------------------------------------------------------------


Hello David,

Today I've picked up 2 QBNews files and I've read them both. Most
interesting stuff! Just the electronic magazine I need! Coming from
the TRS80 Model I computer on which I've learned basic and wrote my
own BBS and videotext systems in basic (the system is still online)
I've entered the PC community and started with RBBS-PC which is, as
you would know, almost completely written in QuickBasic. I've
purchased the package a few years ago and developed a few
administrative applications in it for a friend his firm. At the
moment, due to lack of time, I'm only do a little debugging and
modifying RBBS-PC. I recently discovered a bug (well, I think it is a
bug) which I will give you here:

DEFINT A-Z :' so every variable is by default integer
Single precision! = 3600 * Hour

The maximum value of an integer is 32767 and when you expect
"Hour" can have a maximum value of 24, then the receiving variable
must be a single precision variable 'cause by example 20 * 3600 =
72000. So the construction above looks good but isn't. QuickBasic will
evaluate "3600 * Hour" as an integer expression 'cause both variables
are integers. The result will be an overflow error. I've tested the
same construction on my TRS80 and on the PC in GWBASIC and they both
work fine!!

The solution for this problem is very simple. The technique is called
typecasting.

Single precision! = 3600.0 * Hour :' 3600.0 is of type single
precision

When you enter this in the QB environment, QB will change 3600.0 in
3600! which is the same.

Some undocumented META statements, nice to know:
' $LINESIZE:132
' $PAGE
' $TITLE: 'put the main title here'
' $SUBTITLE: 'put the sub-title here'

Anyway, I hope this is some contribution to the magazine. From
now on I will put the QBNews files in my two BBS systems and will
upload them to some other systems here in Holland. I hope more QBNews
will come from the other side of the ocean. Please respond through
Fidonet if you receive this message. Where I living, well, in a city
called Amsterdam, that's in Holland or The Netherlands and that is in
Europe ('cause many Americans don't know what's on the other side of
the ocean ;-)


The QBNews Page 2
Volume 1, Number 3 May 22, 1990

Cu, Kim Kodde (2:500/41.1433) Holland



Hello David,

A very good initiative! I give my full support for whit it's
worth. I am not a professional programmer and I have no education in
this area, but I have been programming in BASIC for some years for fun
and during that time gone from an VIC64 to an AMSTRAD 6128 to an
Artech 286/AT (and IBM ps 20). I started out with QB 2.00 a couple of
years ago but recently upgraded to 4.50. It is a marvelous difference.

I have also included an extract from a program I have written for
my work. I am a journalist and the program is used to store things we
are to check or cover in the future. Certain things come back
regularly on certain weekdays no matter which date it is. I therefore
needed a simple routine to check which weekday a certain date is so
when the agenda for that date is printed out the correct items
depending on the weekday is included. I found my answer in interrupt
33, function 2a00. The low part of register AX returns a number for
the weekday. 0 for Sunday to 6 for Saturday. This is very good for
the future. If you are interested in checking which day you were born
for example you need something else because the computer only covers
dates from the early 80-ies. I have seen programs in genealogy-
packages which covers this part.

Johan Lindgren
Sundsvall, Sweden


I would be interested in hearing from you. Please write with
comments, suggestions, complaints, or whatever you feel like talking
about. Send it to:

The QBNews
P.O. Box 507
Sandy Hook, CT 06482

















The QBNews Page 3
Volume 1, Number 3 May 22, 1990



----------------------------------------------------------------------
B e g i n n e r s C o r n e r
----------------------------------------------------------------------

BASIC Menuing and Graphics by Ryan Snodgrass

This section of QB News is dedicated to the readers who are
novices to Quick BASIC. In this issue we will talk about creating and
editing simple menu programs and about some of the elementary commands
used in making computerized graphics. The first thing we talk about
is making simple menus. The first step you take in making a menu is
to plan what selections you will have. In our example we will use the
following selections: DIR, make the computer beep, and quit. The next
step is to plan whether to use numbers, letters, or both. In our
example we will use both numbers and letters. The following is an
example program; you may edit it as much as you like.

Menu:
PRINT "(1) - DIR" 'Shows the first selection (DIR)
PRINT "(2) - BEEP" 'Shows the second selection (BEEP)
PRINT "(Q) - QUIT" 'Shows the third selection (QUIT)
MenuInput:
A$=INPUT$(1) 'Waits for the input from the keyboard
IF A$="1" THEN GOTO Selection1 'See if one was pressed
IF A$="2" THEN GOTO Selection2 'See if two was pressed
IF A$="Q" OR A$="q" THEN CLS:END 'See if Q was pressed
GOTO MenuInput 'Go back and wait for another key

Selection1:
SHELL"DIR" 'Do a DIR of the current directory
GOTO Menu 'Redisplay the menu
Selection2:
BEEP 'Produce a beep
GOTO Menu 'Redisplay the menu

-+* To edit selections on this menu do the following: *+-

1) Change the menu listing by replacing one of the selections by
another (i.e. PRINT "(1) - DIR" replace with the following:
PRINT "(1) - DIR/P")

2) Change the selection commands (i.e. change the Selection1 commands
to:
Selection2:
SHELL"DIR/P"
GOTO Menu

-+* To add selections to the menu do the following: *+-

1) After the PRINT "(2) - BEEP" add PRINT "(3) - Your Selection"

2) After IF A$="2" THEN... add IF A$="3" THEN GOTO Selection3

3) After the GOTO Menu on Selection2 add:
Selection3:

The QBNews Page 4
Volume 1, Number 3 May 22, 1990

'Your command
GOTO Menu


The next subject we will discuss is elementary commands for
creating graphics screens. A graphics screen is represented by
a number of pixels or points on the screen, which is referred to as
the resolution of your screen. The first step we must do is select a
screen number which corresponds to your resolution or any other
resolution you can use. The following is a list of screen number and
their attributes:

+-----------+------------------+-------------+------------------+
|Screen #: | # of colors: | Resolution: | Width of Screen:|
+-----------+------------------+-------------+------------------+
| 1 (CGA) | 4 out of 16 | 320x200 | 40 Characters |
| 2 (CGA) | 2 out of 2 | 640x200 | 80 Characters |
| 7 (EGA) | 16 out of 64 | 320x200 | 40 Characters |
| 8 (EGA) | 16 out of 64 | 640x200 | 80 Characters |
| 9 (EGA) | 16 out of 64 | 640x350 | 80 Characters |
| 10 (MONO) | 9 grays | 640x350 | 80 Characters |
| 11 (VGA) | 2 out of 2 | 640x480 | 80 Characters |
| 12 (VGA) | 16 out of 256000| 640x480 | 80 Characters |
+-----------+------------------+-------------+------------------+

The next step is to type: SCREEN (and then the type of graphics
you want). The first and easiest command is LINE. The usage is LINE
(X,Y)-(X,Y),Color (i.e. LINE (100,100)-(200,200),1 would draw a line
from 100,100 to 200,200 using the color 1). Using that in an example
program would be as follows:

SCREEN 1
LINE(100,100)-(200,200),1
END

You can also draw an open rectangle by using LINE (X,Y)-
(X,Y),Color,B or a solid rectangle by using LINE (X,Y)-(X,Y),Color,BF.
The next command we will go into is the DRAW command. The usage is
DRAW "(Attributes)" (i.e. DRAW "U4 R4 D4L4"). The attributes are as
follows: C#=Color Number, U#=Up a number of pixels, D#=Down a number
of pixels, L#=Left a number of pixels, R#=Right a number of pixels,
E#=Up and to the right a number of pixels, F#=Down and to the right a
number of pixels, G#=Down and left a number of pixels, H#=Up and left
a number of pixels, M X,Y=Moves to a certain pixel (i.e. DRAW "M
100,100"). Our example program draws a 3-D box:

SCREEN 1
DRAW "M 100,100 C1 U10 R10 D10 L10 H10 U10 F10 R10 H10 L10"
END

If you have any suggestions as to what you would like to see in
this column, please send them to the QBNews at the address in the
back.


The QBNews Page 5
Volume 1, Number 3 May 22, 1990



----------------------------------------------------------------------
W h o y a g o n n a c a l l ? C A L L I N T E R R U P T
----------------------------------------------------------------------

Directory Swapping by Hector Plasmic

You've all seen professional programs that let you Shell to DOS
and always return you to the proper drive and subdirectory when you
Exit. It's not too hard to implement something similar for your own
QuickBASIC programs. QB doesn't have all the functions you need to do
it, but DOS can lend a hand via Interrupt.

What we need to do is handle all SHELL calls from a common point
(Sub). In this Sub, we'll determine the current drive and
subdirectory, perform the SHELL, then restore the old drive and
subdirectory before Exitting the Sub.

Interrupt 21h function 19h returns the number of the current disk
drive in register .al as a number (0=A, 1=B, etc.).

Interrupt 21h function 47h copies the current directory of the
drive # in .dl (0=default, 1=A, 2=B, etc.) as an ASCIIZ (null
terminated) string into a buffer pointed to by .ds:.si. The pathname
does not include the drive identifier or a leading backslash. An
error can occur if you use an invalid drive specification; this is not
likely since we are using the default drive, but if you are using this
function to attempt to read the default directory on a drive that may
not exist, check the carry flag (IF OutReg.FLAGS AND 1
THEN...oops!). .ax will contain 0Fh (invalid drive spec) if the error
occurs.

Finally, Interrupt 21h function 0Eh sets the default drive to the
drive number passed in register .dx (0=A, 1=B, etc.).This completes
the list of DOS functions we'll need to perform our switching. What
follows is some sample code to show a practical application of these
interrupt functions:

DEFINT A-Z

TYPE RegType2
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

'You must link with QB.LIB (QB.QLB) to use Interrupt functions

DECLARE SUB InterruptX (Intr%, InReg AS RegType2, OutReg AS RegType2)

The QBNews Page 6
Volume 1, Number 3 May 22, 1990

DECLARE SUB DoShell (Before$, Args$, After$)

DoShell "Type EXIT to return", "", "Welcome back" 'Just to test it
END

SUB DoShell (Before$, Args$, After$)'Declare some stuff to use

DIM InReg AS RegType2
DIM OutReg AS RegType2
DIM CurrentDrive AS INTEGER
DIM CurrentDir AS STRING * 64

'Get current disk drive

InReg.AX = &H19 * 256
InterruptX &H21, InReg, OutReg
CurrentDrive = OutReg.AX MOD 256

'Get current directory

InReg.AX = &H47 * 256
InReg.DX = CurrentDrive + 1 'Note adding one to drive for this, or
'could use 0 for default drive
InReg.DS = VARSEG(CurrentDir)
InReg.SI = VARPTR(CurrentDir)
InterruptX &H21, InReg, OutReg

'Do the shell
IF Before$ <> "" THEN CLS : PRINT Before$ 'Optional
SHELL Args$
IF After$ <> "" THEN CLS : PRINT After$ 'Optional

'Change to old disk drive
InReg.AX = &HE * 256
InReg.DX = CurrentDrive
InterruptX &H21, InReg, OutReg
'(InReg.AX MOD 256 is the # of logical drives in the
'system if anyone is interested)

'Change to old subdirectory (Could use Int &H21 func &H3B instead)
ToDir$ = CHR$(ASC("A") + CurrentDrive) + ":\" + LEFT$(CurrentDir,_
INSTR(CurrentDir, CHR$(0)))
CHDIR ToDir$

END SUB










The QBNews Page 7
Volume 1, Number 3 May 22, 1990



----------------------------------------------------------------------
T h e T o o l S h e d
----------------------------------------------------------------------

P-Screen+ and P-Screen Professional by David Cleary

*** IMPORTANT ***
It took me a while to write this review. As you read it, you will
see I have two major complaints concerning these programs. They are
lack of mouse support and an easier user interface. These problems
have been corrected in the latest release. Please see the product
announcement for the new version in this issue.

A program's user interface can be the difference between success
and failure. You could have the best program in the world but if
people don't like using it, it won't succeed. The user interface is
also the part of a program I hate doing the most. I know what I like
in a user interface but I have a hard time translating it into my
programs. I also find the coding of user interfaces very tedious. So,
in this issue, I will review a tool that helps you design user
interfaces quickly and easily.

The product is called P-Screen and is from Pro~Formance. P-
Screen+ is a shareware program while P-Screen Professional is
commercial. This program combines a screen drawing utility, a screen
storage and display utility, and the Professional version adds a
QuickBASIC 3 and 4 code generator and a variety of user input
routines. These programs allows you to create screens and forms, store
them in libraries, and access them from your QuickBASIC programs. We
will examine these features individually.

As a screen generator, I feel that P-Screen has one major flaw.
It lacks mouse support. Personally, I like mice and I don't like
having to navigate the screen with the cursor keys. When you start up
P-Screen Professional, you get a nice title screen that tells you your
options and waits for you to press a key. After you press a key, you
are left with a blank screen and a "What do I do now?" look on your
face. Pressing F1 for help shows you 2 screens of commands but that is
all. To learn how to use P-Screen, you will need a copy of the printed
documentation next to you.

After you start using the commands and learn what they do, P-
Screen is a pleasure to use. It allows you to draw your screens and
store them in either ASCII or a compressed format. P-Screen also
allows you to load screens saved in QB's BSAVE format but won't let
you save them that way. If P-Screen had mouse support and better on
line help, it would be one of the best screen designers available.
Without it though, it still is very good.

Next comes the screen storage and display utility. P-Screen
stores screens on disk in a compressed screen format. The savings in
size is very noticeable the more screens you have in a library. P-
Screen then has two routines that load the screens into an array and
display them. They are written in Assembler and are very fast. These
routines allow you to display full or partial screens anywhere you

The QBNews Page 8
Volume 1, Number 3 May 22, 1990

want. I couldn't find anything to complain about. They are nicely done
and will add a professional look to your programs.

Now to what I think is the best part of P-Screen. The
Professional version comes with a form generator. P-Screen
Professional comes with the ability to create forms and it generates
code that handles user input on these forms. See the program Index.Bas
for an example of the code P-Screen Professional generated for a small
index card screen I created. The user input routine is nicely done in
Assembler and includes a variety of formatting options and masks. Some
of these include Date, Phone Number, Zip Code, Social Sec. Number, and
Currency. With these options, your program makes sure that the user
types in only valid keys and in the format you want. The code that P-
Screen Professional generates is then inserted into your program. This
allows you to create professional looking documents with very little
effort. I am very impressed with these capabilities in P-Screen
Professional.

Two shareware programs you should be aware of are P-Screen and
P~F Presents. P-Screen is the screen drawing and library utility
portion of P-Screen Professional. Gone are the code generator and form
utilities and the ability to display partial screens. My advice to you
is to get the shareware version and try out the screen generator. If
you like using it to create your screens, then you should certainly
buy the professional version. You won't be disappointed.

P~F Presents is a screen presentation system. It lets you take
screens created with P-Screen and create slide shows out of them. This
is great for presentations or prototyping applications before you get
down to writing code. With it, you can create your whole user
interface and let the users give you there comments on it BEFORE you
write any code. This saves a lot of headaches from having to rewrite
your programs when your views on how the program should operate are
different from the intended users.

P-Screen Professional is available from:

Pro~Formance
132 Alpine Terrace
San Francisco, CA 94117
(415) 863-0530

The cost is $49 plus $3 shipping and handling. You also can
download the shareware version of P-Screen and P~F Presents from Peter
Tiffany's BBS, (415) 458-6404 or on CompuServe in the IBMPro and
IBMApp areas. The filenames are PSCRN35.ZIP and PFPRES35.ZIP.









The QBNews Page 9
Volume 1, Number 3 May 22, 1990

Index Manager - B-Tree indexing for QB by David Cleary

Databases are an important part of computers and computer
programming. QuickBASIC has built in capabilities that make it easy to
write a simple database. The problem is, as your database gets larger
and larger, it takes longer to find the information you need. That is
where Index Manager helps out.

Index Manager allows you to build single user ISAM (Indexed
Sequential Access Method) databases using a B+ Tree index. A B+ Tree
is a data structure that allows you to find information quickly
without searching something from start to finish. All Index Manager
does is handle the index of your database allowing you to handle your
datafile as you please. All you do is to associate a unique "Key" with
each record and Index Manager does the rest. This key could be a name
or customer number or what have you. The only thing you have to be
sure of is that each record in your database has it's own unique key.

The specifics of Index Manager are as follows:

1. Create an indexed-access file using the key of your choice.

2. Read any record on you indexed-access files by specifying it's
key.

3. Browse through your indexed-access file by specifying a partial
key.

4. Read your indexed-access file sequentially sorted by key either
forward or backward.

5. Work with up to ten indexed-access files at the same time.

All of Index Manager's functions are incorporated into one call
making it very easy to use. It is also written in assembly language
making it extremely fast and small. It only adds 5k of code size to
your QB programs. It utilizes a large cache buffer to cut down on disk
accesses when searching for a key. This makes it very fast but also
introduces one of it's drawbacks. It uses 24k of string space for it's
buffers. This makes string space kind of tight on large applications.
I would like to see some sort of variable buffer allocation so you
could reclaim the string space while sacrificing some speed.

Although Index Manager is very easy to use and comes with
examples showing how to use every function, it doesn't teach you
database principles. If you know nothing of how to set up good
databases, you could find yourself making errors that could cause your
program to not be as useful as it can. The example programs show you
how the functions work but they are not real world applications. I
would like to see some examples of things like a small phone
directory, ect. that will help someone not familiar with database
programming go in the right direction.

Aside from the string space problem and the lack of good database
examples, I love this program. I received my copy of Index Manager

The QBNews Page 10
Volume 1, Number 3 May 22, 1990

free for the purpose of doing this review. At the time, I had never
written a database program before. As luck would have it, a database
programming application had just come up where I work. I decided that
I would give Index Manager a try in helping me with this project. The
project turned out so well that my company decided to pay for this
copy.

Index Manager is for those people who just want to add fast
indexing to their QB programs. It is an alternative to going out and
spending more money on products like DB-Lib or B-Trieve when you don't
need all the functions of these products. Index Manager is also alot
easier to use than those products and allows you to handle your
datafiles anyway you choose.

Index Manager costs $59 and is compatible with QB 2 to 4.5 and
Bascom 6. A Basic 7 PDS version that supports far strings is in the
works. You can get Index Manager by contacting:

CDP Consultants
1700 Circo del Cielo Drive
El Cajon, CA 90202

619-440-6482
































The QBNews Page 11
Volume 1, Number 3 May 22, 1990



----------------------------------------------------------------------
P r o d u c t A n n o u n c e m e n t s
----------------------------------------------------------------------

P-Screen, P-Screen Professional, P~F Presents

Rob W. Smetana (415) 863 - 0530
Pro~Formance 132 Alpine Terrace San Francisco Ca 94117

Program Name Cost Summary
--------------------- ---- -----------------------------------
P-Screen "Plus" $ 29 Screen Design, Display, Library
system for QuickBASIC 3.0 - 4.x.

P-Screen "Professional" $ 49 PS Pro writes programs for you! and
includes other CALLable routines.

P~F Presents $ 49/$ 79 Presentation system to display
P-Screen's "text" screens -or-
BSAVEd "graphics." See below for
many "uses" for presentations.

P-Screen "Pro" AND Enhanced versions of BOTH.
P~F Presents ($79 ver.) $110

NOTE: P-Screen and P~F Presents are "shareware." Registered
versions cost 1/2 to 2/3 LESS than commercial examples.
Yet they're finely tuned, professional quality programs
that'll save you enormous amounts of time, and give your
programs a professional appearance -- f-a-s-t, easily.

P-Screen and P-Screen Professional are among the most sophisti-
cated "screen management systems" available. They were written
BY Quick- BASIC programmers FOR QuickBASIC programmers (we
support QB 3.0-4.x). P-Screen "+" and P-Screen Pro share most
features. These are described immediately below. Later we'll
explain features that PS Pro adds.

DESIGNING help screens, menus, data entry screens, etc. is a snap.
* Use a mouse or fast keyboard "hot keys" (eg., alt-B = Box).
* Choose options using hot keys or pull down menus.
* Many design options: Boxes, lines, "auto-joining" of lines
and boxes, text, Big! Font text (tm), clipboard, paint,
center, copy, move, erase, WalkAbout, re-color, repeat, add
any Ascii character, view in Monochrome, UnDo, and much more.

LOAD screens for editing from Screen Libraries, ASCII or BSAVE files.
* A RAM-resident program is included letting you: 1) "Capture"
screens from other applications (to later load and edit); and
2) Save ANY text screen in BSAVE format when you need them.

SAVE screens to Screen Libraries, ASCII or "Com" files.
* Screens saved to libraries or Com files are COMPRESSED,
saving disk space and RAM. Libraries store 1-100 screens;
libraries "index" screens, letting us display or edit them fast.

The QBNews Page 12
Volume 1, Number 3 May 22, 1990

* Com screens are "executable!" Run them from batch files or DOS
for instant screens -- complete with color, lines, shading, etc.

DISPLAY screens from your programs fast and easily -- just CALL
two routines we include.

OTHER FEATURES
* Blazing speed! Assembler language routines plus "indexed"
screen libraries mean your programs display screens f-a-s-t!
10-20 screens per second is typical. We've seen 75 per second!
- We've eliminated the disadvantages of storing screens on disk.
- Screen libraries let you keep "screen text" OUT of your
programs, preserving string space and memory. And you can
edit screens WITHOUT changing your programs.
- And there are NO RAM-resident screen loaders to worry about!

* Big! Font (tm) lets you easily add large-character messages.
We include several Big! Fonts. You can create your own Big!
Fonts, or customize them "on the fly."

* Supports 25, 43 or 50 line screen modes.

=================================================================
P-Screen Professional ($ 49)
=================================================================

P-Screen Professional (PS Pro) has all the features of P-Screen
described above, but also saves you enormous amounts of time by:

* Writing your QuickBASIC 4.x programs for you!

* Including several other subprograms you can use in ANY Quick-
BASIC 4.x program you write (most also work with QB 3.0).

In short, you focus on what your programs "look like." Once
you have your screen designed, PS Pro can write your BASIC code.
Just add any routines you need for printing or database manage-
ment. Then compile your programs and away you go.


PS Pro writes your data entry programs for you! Just "mark" a
"field" on your screen and tell PS Pro what "type" of field it is.
* You can create fields with are "editable," and fields which
are "calculated." For calculated fields, enter ANY Quick-
BASIC formula, and PS Pro will handle the calculations for
you and print their results.

* And you can link "help screens" to each field if you like.

* Once you've "formatted" each field on your screen just press
a key. PS Pro writes your program in about 2-3 seconds!


PS Pro lets you choose from among 13 field types.
* Choose a field type and PS Pro formats editing, printing,

The QBNews Page 13
Volume 1, Number 3 May 22, 1990

and displaying the results of "calculated fields" for you.

* Choose field types from among: string, upper case, proper
name, date, phone number, Zip Code, Social Security number,
numeric string, integer, long integer, single precision,
currency and double precision.

PS Pro comes with several subroutines you can use in any program.
These include: rsMinput, OneKey, Mask/StripMask, FormatUsing,
ProperName, rsQprint, Exists, and StripTail. Use them to:
* Handle ALL user input (text, extended keys, printer codes).
Get a single key, or a full line of text. rsMinput offers
the full array of editing features, includes UnDo, AND
offers "masked" input for easy, accurate editing of fields
like Phone Numbers [(...) ...-....] or Zip Codes [.....-....].

* Format text and numbers for easy editing or printing

* Print with assembler speed

* Convert all lower case text to "proper name" format

* Determine if files exist (before you try to open them)

=================================================================
Product Announcement: P~F PRESENTS
=================================================================

P~F Presents (PFP) is our desktop presentation system. It dis-
plays screens from P-Screen's screen libraries. It also displays
BSAVED "graphics" screens. Text, graphics or both; it's your call.

Presentations (or slide shows) can include, among other things:
* Full- or Sub- screen displays.
* Menus, in 3 different styles.
* Display and sound effects.
* Special options including: "If x Goto," "Gosub", "Loop,"
"Pause," "MakeMono" (to display colored screens on
monochrome monitors).
* Timed slides (which pause as long as you want between slides),
-or- slides which wait for the viewer to press a key.

And you can create self-running presentations, or run 'em yourself.

Why would a programmer be interested in a presentation system?
For many of the same reasons programmers like Dan Bricklin's DEMO
program (at about 1/4 the cost)! Plus, P~F Presents offers
many other opportunities as well. Consider these:

PROGRAM PROTOTYPES
* You can create "working prototypes" of programs in minutes or
just a few hours.
* Since presentations can include menus (3 types), display effects,
sound effects, etc. your prototypes can have the "look and feel"
of your actual program.

The QBNews Page 14
Volume 1, Number 3 May 22, 1990

* Letting your clients, customers or potential users see how your
program will look and feel (BEFORE you've written a line of code):
1) Helps you "beta test" your ideas; 2) Helps eliminate time-
consuming re-writes; and, 3) Speeds up program development.
* And creating "mock ups" helps you think through a program's
logic before you write any code.

PROGRAM: DEMOS, MARKETING TOOLS, TUTORIALS
* You can easily and quickly create demos, marketing tools or
program tutorials -- using many of the SAME SCREENS your
programs will display.

As an example, we recently used PFP to create, in 2 hours, a
working prototype of a new program. We had it back to a
prospective client the day after we first talked to him. It
had the complete "look & feel" of the program he wanted. And
creating it helped us think through our program design.






































The QBNews Page 15
Volume 1, Number 3 May 22, 1990



----------------------------------------------------------------------
U n d e r T h e H o o d
----------------------------------------------------------------------

Fast File I/O in QuickBASIC by Ethan Winer

[EDITOR'S NOTE] This article first appeared in the March 1990 issue of
Programmer's Journal. Back issues can be ordered by calling
1-800-234-0386.

Without doubt, one of the slowest operations a program can
perform is saving and loading disk data files. In many cases, this is
dictated by the physical access delay of the disk device, and the time
required to transfer data based on its rotation speed. One exception,
however, is when many reads or writes must be performed on small
pieces of data. For example, it is quite common to save or load an
entire numeric array. In the case of an integer array that contains,
say, ten thousand elements, that many individual calls to the DOS file
I/O services will be needed. Even though DOS is written in assembly
language, it still takes a fair amount of time to process each read or
write request.

One obvious solution is to process the file operation as a single
large read or write. Indeed, I have written assembly language
routines to do just that for use in company's QuickPak Professional
add-on library product. But it is also possible to call QuickBASIC's
internal PUT and GET routines directly. By bypassing the QuickBASIC
compiler and its syntax checking, you can coerce it to read and write
up to 64K of data in a single operation. Larger files can be
accommodated by processing the file in pieces. The trick is to
determine the names of these routines, and the number and type of
parameters they expect to receive.

QuickBASIC versions 4.0 and later contain four different internal
routines for reading and writing binary data. Two of these are meant
for reading data from a file, with one using the current DOS SEEK
location and the other accepting a long integer SEEK argument.
Similarly, there are two separate routines for writing data to disk.

Most of QuickBASIC's internal routines begin with the characters
"B$", which are illegal in a subroutine name. Fortunately, the ALIAS
keyword allows you to declare a procedure with two different names --
the name you will use when calling it from the program, and the actual
name that is made public for the linker. When Microsoft introduced
inter-language calling capabilities in QuickBASIC version 4.00, it
needed a way to allow access to routines written in C. These routines
always start with an underscore character, which is also illegal as a
QuickBASIC procedure name.

The example program shown in Figure 1 declares the four internal
routines as follows: BigSave writes data using the current DOS file
pointer position, and BigSaveS expects a SEEK argument. Likewise,
BigLoad reads from the current file position, and BigLoadS requires an
offset to SEEK to before reading. All four of these routines require
the parameters to be passed "by value", as opposed to "by address"

The QBNews Page 16
Volume 1, Number 3 May 22, 1990

which is BASIC's usual method of passing parameters. This results in
code that is both faster and smaller, because an extra level of
indirection is avoided. That is, the routines can obtain the values
directly from the stack, rather than having to first determine an
address, and then go to that address for the actual value. Even
though BYVAL and SEG *look* like they would result in additional code
being added to a program, they are really just directives to the
compiler.

Before any of these routines may be called, you must open the
file to be read or written for BINARY operation. Then, the first
parameter that each routine expects is the BASIC file number that was
used to open the file. The address parameter is passed as a SEG
value, which means that both a segment and offset are required.
Notice that a file may be loaded to or saved from any area of memory,
by replacing [SEG Address] with [BYVAL Segment, BYVAL Address]. When
SEG is used as part of a CALL statement, the "value" of the variable's
segment is pushed on the stack, followed by the value of its address.
Substituting two separate arguments "by value" is functionally the
same thing as far as the routines are concerned. Also notice that the
internal routine names are not available within the QuickBASIC editing
environment. Therefore, this example program must be compiled to disk
before it may be tested.

In my own informal tests, I have found this technique to be as
much as ten times faster than reading or writing individual array
elements using a BASIC FOR/NEXT loop. The actual savings will of
course depend on the number of elements being processed and their
length in bytes. Unfortunately, this method cannot be used with
QuickBASIC string arrays, because they are not kept in consecutive
memory locations. However, numeric arrays may be accommodated, as
well as any fixed-length or user-defined TYPE array.

It is important to understand that when manipulating a fixed-
length string array, the SEG operator must not be used. Whenever a
fixed-length string or array element is used as an argument to an
external routine, QuickBASIC first makes a copy of it into a regular
string variable. Then, the address of the copy is passed instead.
Since the address of a copy of an array element has no relevance to
the address of the actual array, we must use a different approach. In
fact, there are two possible solutions.

One is to create a TYPE definition that is comprised solely of a
fixed-length string portion. Although the example below assumes a
string length of twenty characters, you would of course use whatever
is appropriate for your program.

TYPE FLen
S AS STRING * 20
END TYPE

DIM Array(1 TO 10000) AS FLen

The second solution is to use a combination of BYVAL VARSEG and
BYVAL VARPTR, to pass the segment and address of the starting array

The QBNews Page 17
Volume 1, Number 3 May 22, 1990

element directly. When QuickBASIC sees VARSEG or VARPTR, it realizes
that you do in fact want the actual segment and address of the
specified array element. Thus, you would use the following syntax
when calling BigSave to save a fixed-length string array:

CALL BigSave(FileNumber, BYVAL VARSEG(Array(First)), BYVAL _
VARPTR(Array(First)), NumBytes)

One final note concerns saving or loading more than 32767 bytes.
QuickBASIC does not support unsigned integers, so you must instead use
an equivalent negative value. This is quite easy to determine, by
simply subtracting 65536 from the required number of bytes. It is a
common trick to avoid negative numbers when calling assembly language
routines by instead substituting a long integer number or variable.
However, that will not work in this case, because two extra bytes will
be pushed onto the stack by the use of BYVAL. Therefore, it is
essential that you specify the correct type of parameters when calling
these routines.

**********************************************************************
Ethan Winer is the president of Crescent Software, and the author
of QuickPak Professional and P.D.Q. He can be reached by calling
Cresent Software at (203) 846-2500.
**********************************************************************

[EDITOR'S NOTE] Source code for this article is contained in
FASTFILE.ZIP.




























The QBNews Page 18
Volume 1, Number 3 May 22, 1990



----------------------------------------------------------------------
P o w e r P r o g r a m m i n g
----------------------------------------------------------------------

How to Make a Self-Cloning Exe in QuickBASIC by Larry Stone

[EDITOR'S NOTE]
All extended ASCII codes have been replaced in the following
article.

Have you ever had the need to create a program that holds a
password away from prying eyes of others? Or, maybe you have
discovered that writing shareware rewards your ego by making your name
familiar to the PC world, but doesn't reward your pocket-book because
most people will register their programs when they get around to it,
which, often-times, is never. Or, maybe you just wish to write a
program that holds it's configuration without having to create a
configuration file.

One of the easiest methods to accomplish the above listed tasks
is to create an EXE file that "clones" information to itself. The
trick is to create a "recognizable" area inside of the EXE itself that
can be quickly read and modified.

What makes this such an easy trick? Well, have you ever used
Vernon D. Buerg's LIST utility to list your EXE's? If you do, you
will notice that towards the end of the program, every string that you
have defined within your program is CLEARLY VISIBLE! What you are
viewing is the EXE's token definition area. When you compile and link
your programs, a token is defined for every string used. For example,
your program might have the code, Strike$ = "Strike any key". When
you list the EXE, it may show something like, 0TStrike any key. The
symbols 0T would be the programs marker to the definition, "Strike any
key". Under no circumstance do you want to change this marker because
really weird results could ensue. However, you can create a string
that contains a marker that is exclusive to your use, i.e.,

Special$ = "<*!@#%>This is my special string"

In the above example, <*!@#%> then becomes your special marker to
the data that immediately follows. Then, all your program has to do
is to look for your special marker and modify the next 25 characters
as needed!

CAUTION! Never, never, never try to reserve a data area for
cloning by defining a string as:

Special$ = "<*!@#%>" + SPACE$(25)
Special$ = "<*!@#%> "

Both of the above examples will *NOT* create the 25 character
data area desired because BC will optimize the SPACE$(25) as a two
byte token!

Let's assume that we need to build a program that needs to hold a

The QBNews Page 19
Volume 1, Number 3 May 22, 1990

user-defined, sixteen character password and a token that determines
whether the EXE is shareware (limited in scope) or registered. Let's
further say that when a registration is received by you, the author,
you then mail that person a key which redefines the definition of a
token so that features not available to the shareware user now become
available. Let's further state that your program is going to hold
configuration information within a 13 byte string.

The first thing to do is to create a unique string inside of the
program itself, as well as, two or three variables shared within the
program. At the top of the program, do something like the following:

DIM SHARED StoredData$, BytesToData&

Now, if your program does not "clone" configuration information
or passwords, then BytesToData& does not need to be SHARED. Rather,
in this case, only the information itself (Shareware/registration key)
needs to be shared. However, for this discussion, we're going for the
entire pie.

Next, someplace within your initialization subprogram (or in your
main module), you should place code such as the following:

SpotForKey$ = "<%*@#!>123456789012345678901234567890"

Now, because your program needs to read itself, modify itself
and, at the same time, hold part of the original string as your
special marker, we need to do the following:

tempKey$ = LEFT$(SpotForKey$, 6)

In this way, no matter what we do to the following 30 character
spaces, <%*@#!> will always remain our special marker that the program
looks for.

Let's build the routine that reads in this data.

'--------------------- Get Special Data Routine ---------------------

DIM SHARED StoredData$, BytesToData&

SpotForKey$ = "<%*@#!>123456789012345678901234567890"

tempKey$ = LEFT$(SpotForKey$, 6) 'Our special marker.

'The Bytes% variable is the number of bytes to read.If your program is
'less than 16000 bytes then this routine adjusts accordingly. Also, if
'a 16K byte "GET" cuts the marker field in two then you need to change
'it to another value, such as 15550.

Bytes% = 16000
BytesToData& = 0
portion& = 1
countToKey% = 0
StoredDataLen% = 30

The QBNews Page 20
Volume 1, Number 3 May 22, 1990


OPEN MyProg$ FOR BINARY AS #1 FiSize& = LOF(1)
'Get the size of the file
DO WHILE NOT EOF(1)
IF Bytes% > FiSize& THEN Bytes% = FiSize&
IF Bytes% + portion& > FiSize& THEN Bytes% = FiSize& - portion&
A$ = INPUT$(Bytes%, 1) 'Read Bytes% number of characters.
countToKey% = INSTR(A$, tempKey$) 'Look for our special marker.
IF countToKey% THEN 'If we found it then process it.
BytesToData& = portion& + countToKey% + 5 'Get past our marker.
SEEK #1, BytesToData& 'Get the data from the file.
StoredData$ = INPUT$(StoredDataLen%, 1)
EXIT DO 'We found it so out of the DO LOOP
END IF

portion& = Bytes% + portion& 'Determine where the next SEEK is.

'If we're within 800 bytes of the end of the EXE then we are past the
'token definition area of the QB program. In this case, we're done.

IF portion& >= FiSize& - 800 THEN EXIT DO

'Move pointer to the next 16000 byte block to read from the file.

SEEK #1, portion&

LOOP
CLOSE #1

'--------------------- End Special Data Routine ---------------------

Now, whenever our program needs to look for a password, a
registration key value, or it's configuration information, it need
only do the following:

Password$ = LEFT$(StoredData$, 16)
RegisValue = VAL(MID$(StoredData$, 17, 1))

IF RegisValue = 7 THEN
PRINT "Shareware Edition"
ELSE
PRINT "Registered Edition"
END IF

ConfigData$ = MID$(StoredData$, 18)

To prove this works, snip out the special routine and add the
following statement at the end of the routine: PRINT StoredData$
Next, compile and link it, then run it. You will see the above string
printed (you will also notice just how fast QB's INSTR function really
is! - It's blazingly fast! - Couple this with BINARY access and you'll
discover that load time is not appreciably degredated). Don't forget
to define MyProg$ as something or you'll get a nasty error message!

Okay, okay, so how do we write new information to our special

The QBNews Page 21
Volume 1, Number 3 May 22, 1990

data area? Simple - just do the following:

Password$ = "Lawrence Stone "
RegisValue$ = "0"
ConfigData$ = "Config Area 1"

OPEN MyProg$ FOR BINARY AS #1
PUT #1, BytesToData&, Password$ 'To write the new password.
RegisValue$ = "0"
PUT #1, BytesToData& + 16, RegisValue$'To create a registered version.
PUT #1, BytesToData& + 17, ConfigData$ 'To clone configuration data.
CLOSE #1

Now, re-run your compiled program and have these values print to
the monitor. Notice how easy it was to change the data?

If you are going to have an external "key" program that "turns
on" the registered version then it needs to simply read in the data
using the same special marker that we created as a marker to search
for. Also, you might wish to make another small program that converts
your pre- defined password and configuration space to spaces.
Otherwise, you need to run your program before you distribute it so
that you can change the password (which is equal to
"1234567890123456") to something like SPACE$(16). In other words,
nullify the temporary string used by our program that forced BC to
give us the data space we needed in the first place.

Now, for demonstration purposes, we have used the default "7" for
indicating that the program is shareware and "0" for registered. I
would recommend that you reserve at least 8 character spaces for this
field because then you can create unique codes for every key and every
user. In this way, your program can look for the key in both itself
and within the key program as well. This would also offer one more
level of safeguards for you.

One final word: Any casual hacker can use LIST to find your
password if you leave it in it's native ASCII. You should consider a
routine that converts the appearance of your data so that it looks
like the rest of the binary code. Routines can be as simple as taking
each character in the strings and adding 100 to their ASCII value for
writing, then, subtracting 100 from their ASCII value for reading, to
some truely cryptive procedure, depending on how sensitive you want
the information contained therein to remain.

**********************************************************************
Larry Stone is President of LSRGroup and is involved in writing
software for marine and aquatic research. He can be reached at
LSRGroup, P.O. Box 5715, Charleston, OR 97420, or in care of this
newsletter.
**********************************************************************

[EDITOR'S NOTE] The file CLONE.BAS contains a slightly modified
version of Larry's code above. I have modified it to make it easier to
add to your program. It is contained in CLONE.ZIP.

The QBNews

  
Page 22
Volume 1, Number 3 May 22, 1990

In Search of the Elusive UEVENT by Jim Mack

QB allows you to trap a number of different "events", such as the
TIMER tick, the arrival of a byte in a COM buffer, the press of a
specific KEY, and so on. It does so by adding extra code after
each statement (/V) or each line (/W) which checks the state of flags
associated with enabled events. Special handlers deal with the
actual interrupt and set these internal flags as appropriate.

This is a three-stage process: first, an interrupt (an event)
occurs and is handled quickly by the QB runtime, which sets the flag
and variables associated with the event. This happens behind your
program's back, as it were. Second, when the current line or
statement completes, QB checks the flags to see if any active events
(those for which you have executed "ON xxx GOSUB" and "xxx ON"
commands) have occurred. Third, on discovering such a condition QB
executes the GOSUB code you wrote to deal with it.

The only area where UEVENT differs from events like KEY is in the
first part of the first step. In defining a UEVENT, _you_ take
responsibility for dealing with the interrupt, and for notifying the
QB runtime that such an event has occurred. From that point on, the
action is exactly the same.

The difference between invoking a GOSUB via SetUEvent (or any
trap) and calling it directly is that when you invoke it, it's
executed only when QB gets around to it, and only if UEVENT ON is
currently in effect. A side effect of this is that you may "lose"
events if more than one occurs between occasions when QB checks its
internal flags.

This whole business of interrupts can be broken down in several
ways: shared vs. exclusive vs. chained, or software vs. hardware, and
so on. The code packages included here give examples of two common
combinations.

You can trigger a UEvent in QB with no interrupt at all, by just
saying "CALL SetUEvent". In MASM, declaring SetUEvent as an EXTRN far
procedure lets you do the same thing: CALL SetUEvent. In C, you'd
declare "setuevent" as a void far external function and then reference
"setuevent()" to cause your QB handler to be invoked. Simple... and
practically useless by itself. You need to combine this with a
software or hardware interrupt.

>> "Software interrupts" are really misnamed: they have more in
>> common with a subroutine call than with a hardware interrupt.
>> Since they occur under direct program control, there's nothing
>> unexpected or asynchronous about them. They do however use
>> the same table of vectors that the hardware interrupts use.

A small step up is the exclusive "true" software interrupt. This
involves taking over an unused interrupt vector, writing a tiny MASM
routine which intercepts INTs directed at this vector and performs a
CALL SetUEvent. There's no reason to take this extra step unless
you're working with a canned other-language program which must use a

The QBNews Page 23
Volume 1, Number 3 May 22, 1990

pre-defined INT to access your code. If you're using DOS 3.x, this
can be done in exactly the same manner as the "chained" software
interrupt described below, since what you're chaining onto is a
pre-defined Dismiss This Interrupt routine.

>> A "vector" in this context is a memory location reserved by
>> the computer as a pointer: it contains the address of a routine
>> intended to service an interrupt. There are 255 such vectors in
>> the PC, occupying the memory from 0000:0000 through 0000:03FF.
>> Eight of these (sixteen in the AT) are reserved for use by the
>> hardware Interrupt ReQuest lines, or IRQs. When an enabled
>> interrupt occurs, the PC stops what it's doing and executes the
>> routine whose address is stored in the appropriate vector.

Next most complicated is the chained software interrupt. One
example of an existing software interrupt is the BIOS disk service,
which uses INT 13H. If you wanted your handler to be invoked whenever
disk activity occurred, you'd chain onto this interrupt vector and
monitor the registers using MASM. When an event of interest occurred,
you'd "CALL SetUEvent" to notify QB. In any case, you'd pass the
interrupt along to the original INT 13H handler. Closely related to
this is the chained hardware interrupt. The setup is exactly the
same: hook the interrupt vector, monitor the registers, etc. All
other details are taken care of by an existing handler.

The code in CHNEVENT.BAS is an example of a chained handler which
will work for any hardware or software interrupt. The assumption is
that you're just monitoring existing events (and sometimes activating
SetUEvent), but not touching any hardware. In the example we monitor
INT 9, the keyboard interrupt, but you can monitor almost any of
the256 vectors by replacing "9" with the appropriate number. Try an
experiment: replace the INKEY loop with a LINE INPUT statement. If
you can explain what happens, you've grasped the essentials of QBevent
handling.

>> "Hooking" a vector means only that you store the address of your
>> own service routine in the vector. To facilitate cleanup, it's
>> usual to first retrieve and store the existing contents of the
>> vector so that they can be replaced on exit. If you're "chaining"
>> onto this vector, then you'll also use that original address when
>> your routine is finished, by performing a JMP directly to it.
>> Since this can happen to several routines in sequence, it's easy
>> to see why it's known as chaining.

The next step up in complexity (and it's a pretty big step) is
the exclusive hardware interrupt. Here, you're responsible for all of
the nitty-gritty of the PC hardware, in addition to any details
associated with the hardware device. You must program the 8259A
Programmable Interrupt Controller to allow interrupts on your IRQ
level, then issue a command to clear the PIC when you service an
interrupt. These must be done in MASM, as your QB event handler will
not be executed in a timely fashion and so cannot be relied on to take
care of these high-speed events. The code in EVENTHDW.ASM shows how
to deal with an event occurring on an arbitrary IRQ line (determined
at install time), but because we aren't dealing with a real device

The QBNews Page 24
Volume 1, Number 3 May 22, 1990

here, the specific instructions for the interrupting hardware can only
be hinted at.

>> Each hardware IRQ line is intimately tied to a vector: in the
>> case of the lower IRQs (0-7) the INT number (the vector number) is
>> simply the IRQ number plus 8. That's why the KB interrupt, which
>> uses IRQ 1, is vectored through INT 9.

Slightly more complicated is the shared hardware interrupt. In
order for two hardware devices to share an IRQ line, there must be
away to determine which device requested service by interrupting. An
example of sharing an interrupt might be COM1 and COM3, which both use
IRQ4 and hence INT 0CH. When an interrupt occurs on IRQ4, the COM3
service routine gains control and examines a register in the UART it's
responsible for to see if that UART caused the interrupt. If it
didn't, control is passed to the COM1 service routine. I haven't
included a specific example of adding a shared handler, but if you
need one and can't figure it out from the code shown, you can contact
me and I'll try to help.

In addition to the above, whenever you take over an interrupt
vector you must somehow put things back in order when your program
terminates. At a minimum this means restoring the original contents
of the vector; for hardware interrupts, you must also restore the
8259A Interrupt Mask Register bit to the state in which you found it.

To make this process a bit more automatic, QB includes the
B_OnExit routine. Any running BC program takes over a number of
interrupt vectors for its own use (for example, BC math functions
invoke INT 04H whenever an overflow occurs) which must be restored on
any exit, normal or abnormal. BC and QB provide B_OnExit as an
extension of this internal cleanup. You still must write the code to
do the actual restoring of vectors, etc., but BC can call that code
automatically on *any* exit, even an error crash, if you "register
"your routine via B_OnExit. Each of the included code packages uses
B_OnExit in this way.

**********************************************************************
Jim Mack is a programmer specializing in real-time systems for the
entertainment industry. He can be reached via CIS ID 76630,2012 on
the MSSYS forum in the BASIC or MASM sections, or at Editing Services
Co., PO Box 599, Plymouth MI 48170, (313) 459-4618
**********************************************************************

[EDITORS NOTE]
For some reason, the program CHNEVENT.BAS will cause my computer
to crash when run in the QB 4.5 enviroment. This is not the case when
run in the QBX (BC7) enviroment. Caution is advised if you try to run
this in the enviroment. Source code for this article is contained in
UEVENT.ZIP.





The QBNews Page 25
Volume 1, Number 3 May 22, 1990



----------------------------------------------------------------------
S o m e A s s e m b l y R e q u i r e d
----------------------------------------------------------------------

Assembler Programming for QuickBASIC by Tom Hanlin

Perhaps you already know a little bit about programming in
assembly language, or perhaps you've never given it much thought.
After all, it's supposed to be frighteningly difficult to write
assembler programs, and you've already got QuickBASIC anyway.

Well, it's true that there's a lot of work involved in writing
large programs in assembly language. If you keep to the small stuff,
however, there's actually very little to it. One of the easiest and
most rewarding uses for assembly language lies in writing routines
that can be called from a higher-level language like QuickBASIC. This
can give you entirely new capabilities or make your existing programs
smaller and faster. By mixing the capabilities of a low-level
language like assembler with a high-level language like QuickBASIC,
you can gain a lot of flexibility when you need it, without
sacrificing the ease of use of good ol' BASIC.

Microsoft's documentation on mixed-language programming is rather
daunting. It's not exactly clear and the examples never seem to cover
quite what you had in mind. Once you understand a few simple rules,
though, you'll see that adding assembler routines to your QuickBASIC
programs can be readily accomplished.

I'm going to assume you have some notion of how to program in
both QuickBASIC and assembly language, since explaining an entire
language would be a bit more than a single article could cover! With
that in mind, let's take a look at the basic rules of writing assembly
routines for QuickBASIC and follow that up with the code for a working
routine.

The first thing you need to know is which registers you can use.
The answer is, "all of them." However, certain registers must be
preserved for BASIC, so if you use them, you must restore their
original values before returning to the main BASIC program. The
registers that must be preserved are SI, DI, BP, and DS. You also
need to preserve the stack (SS and SP) and direction flag. The
direction flag must always be "forward" when you exit, so if you
change it using the "STD" instruction, be sure to restore it using the
"CLD" instruction.

Believe it or not, that's most of what you need to know right
there. The other important thing to know is how to pass parameters to
or from the assembler routine. I'll keep it simple by assuming you
use the standard convention, which is "pass by reference", rather than
"pass by value", which has to be explicitly declared.

What do I mean by "pass by reference?" I mean that, instead of
getting the actual value of a parameter, your routine gets the address
of that parameter and has to look up the value. It's useful to have
the address of the parameter, since that means you can return a value

The QBNews Page 26
Volume 1, Number 3 May 22, 1990

by storing it in the parameter's address.

Integral numbers are stored simply. For integers, the address
you are given points directly to the integer (a word). If you use
long integers, the address points to the long integer (a doubleword).

Strings are stored in a slightly more complex fashion. The
address you are given points to a "string descriptor". This
descriptor is composed of two words, with the first giving the length
of the string and the second the address of the string. You are not
allowed to change the length or address of the string in your
assembler routine, although you may change the string itself.

I won't go into single precision or double precision numbers,
because they are rather tricky to handle in assembler. I won't go
into arrays, TYPEd values, or fixed-length strings here either, to
keep it reasonably brief. Perhaps in a later article...

Parameters are passed on the stack, starting at offset 6 (six)
for the -last- parameter and going up by two as you move towards the
first parameter.

Finally, you need to end your routine with a special "RET" opcode
that will clean the parameters off the stack for QuickBASIC. The RET
must have a number after it which is twice the number of parameters
passed to the routine.

Clear as mud? Well, perhaps the example routine will help show
what I'm talking about. The DOSVER.ASM file contains the source code.
To assemble it just type: ASM DOSVER; (where "ASM" is the name of your
assembler: MASM, TASM, or OPTASM)

Convert the resulting DOSVER.OBJ file to a library so you can
easily use it both from the command line and QB environment:
LIB DOSVER; (this creates DOSVER.LIB)
LINK DOSVER/Q,,NUL,BQLB45; (this creates DOSVER.QLB)

If you are using QuickBASIC 4.0, change the BQLB45 to BQLB40. If
you are using QuickBASIC 4.0a or 4.0b, change it to BQLB41.

You can now use the DOSVER routine from the QB environment by
specifying the library name when you start QuickBASIC: QB /L DOSVER

Use of the DECLARE statement is optional, but it will help catch
any errors you might make when calling DOSVER. Use this: DECLARE SUB
DOSVER(VERSION$, MAJORV%, MINORV%)

Before calling the routine, you must set the VERSION$ string to
at least four characters, since the routine is not allowed to change
the length of the string. Call the routine like this:
VERSION$ = SPACE$(4)
CALL DOSVER(VERSION$, MAJORV%, MINORV%)

Typical results will be "3.11" for VERSION$, 3 for MAJORV%, and
11 for MINORV%.

The QBNews Page 27
Volume 1, Number 3 May 22, 1990


In later articles, if there is any interest in it, I'll explain
how to handle arrays, TYPEd variables, and fixed-length strings, and
also how to pass values back from functions rather than using
subprograms.

**********************************************************************
Tom Hanlin is the author of the very popular ADVBAS library for
QuickBASIC. His new shareware library is called BASWIZ. He can be
reached through the QUIK_BAS echo on Fidonet or in care of this
newsletter.
**********************************************************************

[EDITOR'S NOTE]
The archive ASMREQ.ZIP contains the assembler source code plus an
assembled .OBJ file for the routine contained in the article.







































The QBNews Page 28
Volume 1, Number 3 May 22, 1990



----------------------------------------------------------------------
A n d I H e a r d i t T h r o u g h t h e G r a p e v i n e
----------------------------------------------------------------------


From: Mike Sinak
To: All
Subj: PEEK & POKE

I am just beginning to study DOS and BIOS calls and don't understand
how to do it at all. Does anybody have any info on the PEEK and POKE
usage? Like if you want to peek and poke to turn caps lock on and
off, how in the world do you know what address to peek and poke? I'm
lost! I would like to learn more about this stuff and get some kind
of comprehensive list of peeks and pokes.

Also I learned that at DEF SEG 0 you can turn on and off caps lock by
POKE &H0417, PEEK &H0417 OR &H40 (to turn on)

and

POKE &H0417, PEEK &H0417 AND &HBF (to turn off)

Now how in the world did they know that? The AND and OR statments are
supposed to be turning on and off bit 6 of the &H0417 address. How
did they know these particular numbers turn on and off bit 6? Better
yet, how did they know that bit 6 had to be turned on and off at this
particular address to turn caps lock on and off. Is there a pattern?

Obviously my ignorance is being displayed. Go ahead and laugh <grin>
but I would sure like to know more about this.

ANY HELP?


From: Robin Hunziker @ 965/1
To: Mike Sinak
Subj: Re: PEEK & POKE

Perhaps your best bet for figuring out how DOS operates is by
purchasing the following book:

Advanced MSDOS Programming
2nd Edition
Ray Duncan
Microsoft Press
ISBN 1-55615-157-8

For example, on page 582 the book discusses exactly your area of
question in easy-to-understand tabular format. It discusses how to
call Int 16H Function 02H to "get keyboard flags". Although the front
cover states that it is "The Microsoft guide for Assembly Language and
C programmers", it is very relevant to much of the discussion in this
echo.


The QBNews Page 29
Volume 1, Number 3 May 22, 1990


From: David Martin
To: Mike Sinak
Subj: Peek & Poke

A better way to set upper and lower case is the following:
Lower Case: POKE 1047, PEEK(1047) AND 191
Upper Case: POKE 1047, PEEK(1047) OR 64
Lots of stuff can be done with poke such as disable the CTRL-BREAK,
turn number lock key on and off, read hardware configurements, read
size of RAM. The list goes on and on. If you want a list of the
commands let me know. These commands must be preceded by: DEF SEG =
0. Let me know what kind of stuff you are working with so I can give
you the commands.


From: Tom Hanlin
To: Mike Sinak
Subj: Re: PEEK & POKE

In order to understand bit numbering, you need to convert the
number to binary. Bits are numbered from right to left, with the
lowest bit being number zero. So:
&H40 = 64 = 0100,0000b (note that bit 6 is turned on)
&HBF =191 = 1011,1111b (note that all bits except 6 are turned on)
when you OR a number with another number, the result contains all of
the bits that were turned on in the first number OR in the second
number. When you AND two numbers, the result contains all of the bits
that were turned on in the first number AND in the second number. So,
to turn on a specific bit, you OR with a number that has only that bit
turned on (which leaves the other bits in the original number alone).
To turn a specific bit off, you AND with a number that has only that
bit turned off (which leaves the other bits in the original number
alone). See?

Knowing which memory locations have special purposes, such as
keeping track of Caps Lock and Num Lock, comes from (mostly) IBM's
listing of the system BIOS. Many reference books contain the
information in a decoded format, so you can use it without having to
understand the BIOS at all. If you can find a copy, COMPUTE!'s
"Mapping the IBM PC and PCjr" is an excellent guide to this sort of
thing, although there are many others.

You may also want something like Norton's guide to assembly
language, to give you some idea about converting numbers between hex,
decimal and binary, and how PC memory mapping works. Things like...
DEF SEG=0:PRINT PEEK(&H417)
is the same as
DEF SEG=&H40:PRINT PEEK(&H17)


From: David Martin
To: Mike Sinak
Subj: Peek & Poke


The QBNews Page 30
Volume 1, Number 3 May 22, 1990

The following list is some things that can be done with peek & poke.
KEYBOARD KEYS:
PEEK (1047) AND 8: value 8 if ALT is pressed.
4: CTRL
2: 2 LEFT SHIFT.
1: 1 RIGHT SHIFT.
3: 3 NIETHER SHIFT.
PEEK (1048) AND 4: 4 PRINT SCREEN KEY.
PEEK (1047) AND 64 0 if in LOWER CASE.
64 UPPER CASE.
POKE 1047, PEEK(1047) AND 191 specify LOWER CASE.
OR 64 specify UPPER CASE.
FOR J=0 TO 3:POKE (108+J),PEEK(112+J):NEXT: DISABLE CTRL-BREAK
CONFIGURATION OF COMPUTER:
(PEEK(1041) AND 192)/64: number of PRINTER ADAPTERS.
(PEEK(1040) AND 1)*(1+PEEK(1040)/64 number of DISK DRIVES.
(PEEK(1041) AND 14)/2 number of RS232 ports.
PEEK(1043)+256*PEEK(1044) size of RAM.
NUMBER LOCK KEY:
PEEK(1047) AND 32 0 if OFF, 32 if ON.
POKE 1047,PEEK(1047) AND 223: turn NUM LOCK key OFF.
32: turn NUM LOCK key ON.
Alot of these commands are abreviated. If nothing appears below a
line just type in the line on top of what you want along with what is
changed. Got it? Let me know if you have any questions. Glad to help!
All lines must be preceded by: DEF SEG =0 (Only one DEF SEG = 0 at
the beginning of the program)


From: Ronny Ong
To: Mike Sinak
Subj: Re: PEEK & POKE

Mike, I also have Duncan's book and find it useful, but I do happen to
use a number of languages including assembler, C, and QuickBASIC.

Let me suggest "DOS Programmer's Reference" (2nd Edition), by Terry
Dettmann, Revised by Jim Kyle. It is published by Que
ISBN 0-88022-458-4. List price is $27.95 U.S. This book contains
roughly the same type of information as Duncan's, but it has specific
QuickBASIC 4.xx examples, so it's probably easier to use for the
QuickBASIC programmer with less experience in assembler and C.



From: Ronny Ong
To: Mike Sinak
Subj: Re: Peek & Poke

Hi, Mike. You might try getting a general intro/computers type of
book and brushing up on binary numbers. All digital computers of
today do everything in binary, or base 2. In others words, computers
are simply made up of billions of tiny on/off switches. "On"
represents the binary digit (or "bit" for short) of "1" and "off"
represents "0."

The QBNews Page 31
Volume 1, Number 3 May 22, 1990


When you see "POKE xxx, PEEK(xxx) AND 64," here's what happens (I've
simplified this slightly):
Let's say that PEEK(xxx) currently contains the byte value 98. In
binary, that's 01100010. The decimal number 64 is 01000000 in binary.
The "AND" operator means to produce a result which has a "1" in every
position which is "1" in both of the operands and has a "0" for all
other positions.

01100010 <--- 98
01000000 <--- 64
--------
01000000 <--- Result of 64, to be POKE'd back into memory location xxx

The end result is that all bits were turned "off" (set to 0) except
that one bit which corresponded to 64. So, essentially, the AND 64
"stuff" you see is manipulating individual on/off switches inside the
computer.

A good book will walk you through conversion between binary and
decimal. Also, you'll see that hexadecimal ("hex") numbers which
you've probably come across, are shorthand forms of binary. Good
luck!


[EDITOR'S NOTE]
The purpose of this conference is to discuss Microsoft QuickBASIC
and related applications and utilities. SysOps looking for a Group
Mail or EchoMail link into QUIK_BAS should contact the Alliance node
known as 520/323, the FidoNet node known as 107/323, or the Good Egg
Net Node known as 9230/323, or simply 1-201-247-8252 for information
on local feeds.























The QBNews Page 32
Volume 1, Number 3 May 22, 1990



----------------------------------------------------------------------
S w a p S h o p
----------------------------------------------------------------------

***** Screen Scrolling with Call Interrupt

DECLARE SUB scroll.ner (rader!, left!, up!, right!, down!)
DECLARE SUB scroll.up (rader!, left!, up!, right!, down!)

'To demonstrate the use of interrupt 10H ah=06
'To scroll any part of the screen up or down
'QB must be invoked with the /l switch. In QB.BI the RegType is
'defined and the INTERRUPT-Call set up.
'By Johan Lindgren (BBS:+46 60115371)

'$INCLUDE: 'QB.BI'

DIM SHARED inregs AS RegType, outregs AS RegType

'---------------------------------------------------------------------
' Fill the screen with numbers to indicate where scrolling takes place
'---------------------------------------------------------------------

FOR i = 1 TO 24
FOR j = 1 TO 40

PRINT USING "##"; i;

NEXT j
NEXT i

'----------------------------------------------------------

' Do one scroll up first. Give values and call the sub
'----------------------------------------------------------

rader = 2: up = 5: left = 5: down = 10: right = 20

scroll.up rader, left, up, right, down

'------------------------------------------------------------

' Do a scroll down. Give values and call the sub
'------------------------------------------------------------

rader = 3: up = 10: left = 35: down = 15: right = 70

scroll.ner rader, left, up, right, down


'END of demo.

SUB scroll.ner (rader, left, up, right, down)

'-----------------------------------------------------------

The QBNews Page 33
Volume 1, Number 3 May 22, 1990

'First put up and down in right hexadecimal postion
'Then enter the values into the registers and call interrupt
'------------------------------------------------------------

up = up * 256
down = down * 256

inregs.ax = &H700 + rader
inregs.cx = up + left
inregs.dx = down + right


CALL INTERRUPT(16, inregs, outregs)


END SUB

SUB scroll.up (rader, left, up, right, down)

'-----------------------------------------------------------
'First put up and down in right hexadecimal postion
'Then enter the values into the registers and call interrupt
'------------------------------------------------------------


up = up * 256
down = down * 256

inregs.ax = &H600 + rader
inregs.cx = up + left
inregs.dx = down + right


CALL INTERRUPT(16, inregs, outregs)


END SUB


















The QBNews Page 34
Volume 1, Number 3 May 22, 1990

***** Getting the Day of the Week with Call Interrupt

DECLARE FUNCTION dag$ (nr!)

' This is just an example to illustrate the use of an interrupt
' to get the day of any date.
' By Johan Lindgren (+46 60-121497 (BBS System))

'$INCLUDE: 'QB.BI'

DIM inregs AS RegType, outregs AS RegType

inregs.ax = &H2A00

spardat$ = DATE$

CLS
PRINT
PRINT "This is just to show the function of interrupt 33,function 2a"
PRINT "It works well for our time and the future."
PRINT "If you need this function for old dates, you have to use"
PRINT "something else."

ON ERROR GOTO fel.datum 'In case you enter a bad date

main:

svar$ = " "
DO UNTIL svar$ = ""
INPUT "Input the date to check in the format MM-DD-YEAR"; svar$
IF svar$ <> "" THEN
DATE$ = svar$ 'Set the date we want to check
'Call the interrupt to get the day
CALL Interrupt (33, inregs, outregs)
dagnr = outregs.ax MOD 256 'Extract the daynumber returned
DATE$ = spardat$ 'Restore proper date

SELECT CASE dagnr
CASE 0
dag$ = "Sunday"
CASE 1
dag$ = "Monday"
CASE 2
dag$ = "Tuesday"
CASE 3
dag$ = "Wednesday"
CASE 4
dag$ = "Thursday"
CASE 5
dag$ = "Friday"
CASE 6
dag$ = "Saturday
END SELECT

PRINT "The day of the week for ";svar$;" is ";dag$

The QBNews Page 35
Volume 1, Number 3 May 22, 1990

PRINT
END IF






LOOP

CLS
END

fel.datum:
RESUME main








































The QBNews Page 36
Volume 1, Number 3 May 22, 1990

***** A YES/NO response DEF FN

' TO: DAVID CLEARY 76510,1725
' FROM: BEN HARTLEY 70033,2612
' SUBJECT: For QBNews -- CHOOSE.BAS -- (FUNCTION)
'===================================================================
'Dear David,
'
' Liked your newsletter, which I downloaded from Exec-PC BBS.
' I'm not what you might call an experienced programmer, but the
' enclosed function might be of interest.
' There are LOTS of routines around that allow selection of
' a simple "Yes" or "No" response. There are times, however, when
' the flow of a program is improved if the choices are something
' other than "Y" or "N". That's where this one comes in. If there
' are no more than two choices, then they can be explicitly stated.
' Whether this is any faster, or, heaven save us all, more "elegant"
' than using SELECT CASE, I haven't the foggiest. I do know that it
' works!
' You can load this entire file into your QuickBASIC editor,
' and run it. It was originally written in QB 3.0, but seems to
' run fine in QB 4.5 --
' Hope this is something along the lines of what you're
' looking for.
'
' Ben Hartley
' Jaffrey, NH

'-------------------------------------------------------------------
DEF FNChoose (prompt$, Response1$, Response2$)
R1$ = UCASE$(LEFT$(Response1$, 1))
R2$ = UCASE$(LEFT$(Response2$, 1))
alpha$ = R1$ + R2$

PRINT prompt$; " ("; R1$; " or "; R2$; ") "
reply$ = ""
charPos% = 0
WHILE charPos% = 0
reply$ = UCASE$(INKEY$)
IF (reply$ <> "") THEN
charPos% = INSTR(alpha$, reply$)
IF (charPos% = 0) THEN BEEP
END IF
WEND
reply$ = UCASE$(MID$(alpha$, charPos%, 1))
FNChoose = (reply$ = R1$)
END DEF
'-------------------------------------------------------------------
COLOR 15, 1
beginhere:
CLS
LOCATE 10, 5
PRINT "Which option do you wish..."
LOCATE 12, 7
IF FNChoose("...The First or the Second ?", "First", "Second") THEN

The QBNews Page 37
Volume 1, Number 3 May 22, 1990

PRINT
PRINT TAB(20); "You chose the First Option."
ELSE
PRINT
PRINT TAB(20); "You chose the Second Option."
END IF
LOCATE 16, 7
IF FNChoose("Do It Again or Quit ?", "Again", "Quit") THEN
GOTO beginhere
END IF
PRINT
PRINT TAB(30); "**** All done! ****"
END










































The QBNews Page 38
Volume 1, Number 3 May 22, 1990

***** A Powerful Replacement for INPUT

'SYNTAX: Enput$(Row%,Col%,FLen%,Default$,Allow$,EndKeys$,ExitKey$)
'WHERE: Row% & Col% - where input to occur
' FLen% - Field Length - maximum allowable
' Default$ - Field's beginning default value
' Allow$ - Character Set allowable for input
' EndKeys$ - Keystrokes acceptable for finishing.
' SUCH: [Enter] is CHR$(13)
' [Esc] is CHR$(27)
' [F1] is CHR$(255)+CHR$(59)
' [Ctrl+PgUp] is CHR$(255)+CHR$(132)
' ... etc
' ExitKey$ - The keystroke (from above EndKeys$)
' which was struck at function end.
'SIMPLEST USE: A$ = Enput$(0,0,0,"",","",Any$)
' Will default to cursor's current row/col, length is
' autoset to right margin, allowable chars are ALL,
' only ending key is [Enter].
'==========================================> Frederick Volking

FUNCTION Enput$ (Row%, Col%, FieldLen%, Default$, AllowCharsMask$_
,EndingKeys$, ExitKey$) STATIC

SHARED ScreenWidth%, BackSpaceKeyStrokes$, EmptySpaceChar$

' verify SHARED globals are set

IF ScreenWidth% = 0 THEN ScreenWidth% = 40
IF BackSpaceKeyStrokes$ = "" THEN BackSpaceKeyStrokes$ = _
CHR$(8) + CHR$(255) + CHR$(75)
IF EmptySpaceChar$ = "" THEN EmptySpaceChar$ = CHR$(254)

' If not specified then supply defaults to incomming vars

IF AllowCharsMask$ = "" THEN AllowCharsMask$ = CHR$(34) +_
" !#$%&'()*+,-./0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]_
^_abcdefghijklmnopqrstuvwxyz|~"

IF EndingKeys$ = "" THEN EndingKeys$ = CHR$(13)
IF Row% = 0 THEN Row% = CSRLIN
IF Col% = 0 THEN Col% = POS(0)
IF FieldLen% = 0 THEN FieldLen% = ScreenWidth% - Col%
IF LEN(Default$) > FieldLen% THEN Default$ = LEFT$(Default$_
, FieldLen%)

' define internal defaults

CharsCollected% = 0
ReturnVar$ = ""
' begin main loop
DO
NotDone% = 1
IF CharsCollected% = 0 THEN
LOCATE Row%, Col%, 0

The QBNews Page 39
Volume 1, Number 3 May 22, 1990

PRINT Default$ + STRING$(FieldLen% - LEN(Default$), _
EmptySpaceChar$);
END IF
IF CharsCollected% = FieldLen% THEN
LOCATE Row%, Col% + CharsCollected% - 1, 1
ELSE
LOCATE Row%, Col% + CharsCollected%, 1
END IF
DO
KeyStroke$ = INKEY$
LOOP WHILE KeyStroke$ = ""
IF LEN(KeyStroke$) = 2 THEN KeyStroke$ = CHR$(255) + _
RIGHT$(KeyStroke$, 1)
IF INSTR(AllowCharsMask$, KeyStroke$) > 0 THEN
IF CharsCollected% < FieldLen% THEN
IF CharsCollected% = 0 THEN
PRINT STRING$(FieldLen%, EmptySpaceChar$);
LOCATE Row%, Col%, 0
END IF
ReturnVar$ = ReturnVar$ + KeyStroke$
CharsCollected% = CharsCollected% + 1
PRINT KeyStroke$;
END IF
ELSE
IF INSTR(BackSpaceKeyStrokes$, KeyStroke$) > 0 THEN
IF CharsCollected% > 1 THEN
CharsCollected% = CharsCollected% - 1
LOCATE Row%, Col% + CharsCollected%, 0
PRINT EmptySpaceChar$;
LOCATE Row%, Col% + CharsCollected%, 1
ReturnVar$ = LEFT$(ReturnVar$, CharsCollected%)
ELSE
CharsCollected% = 0
ReturnVar$ = Default$
END IF
ELSE
IF INSTR(EndingKeys$, KeyStroke$) > 0 THEN
ExitKeys$ = KeyStroke$
NotDone% = 0
ELSE
SOUND 500, 1
END IF
END IF
END IF
LOOP WHILE NotDone%
Enput$ = ReturnVar$
END FUNCTION








The QBNews Page 40
Volume 1, Number 3 May 22, 1990

***** Windowing Routines with Shading

'FRAME.BAS test program
'Lawrence Stone, 1990
'
'Purpose: Demonstrate the versitility of the Frame subprogram.
'Note: I built this routine just for the fun of it. I designed it to
'be easy to use and versitile. This demonstration program simply makes
'six calls to the frame subprogram-the subprogram does the rest.


DECLARE SUB Frame (TopRow%, leftCol%, botRow%, rightCol%, boxType%,_
boxFg%, boxBg%, filFg%, filBg%, fillChar%, shadow%, header$)

COLOR 12, 4 'Set a fancy color
FOR A% = 1 TO 25
LOCATE A%, 1
PRINT STRING$(80, 206); 'Draw a complex background
NEXT

'Test the Frame subprogram
Frame 5, 20, 21, 75, 2, 15, 3, 9, 7, 178, -1, "This is a Test"

'Overlay the 1st box with a smaller 2nd box
Frame 4, 3, 10, 36, 1, 15, 6, 6, 0, 219, -1, "This is Test 2"

'What the heck, let's draw another box
Frame 13, 33, 22, 70, 3, 14, 2, 15, 3, 221, -1, "This is Test 3"

'What the heck, draw box #4 without a header
Frame 15, 5, 19, 65, 4, 13, 0, 0, 7, 32, -1, ""
LOCATE 17,14:PRINT "Box #4 has a shadow but doesn't have a Header."

'What the heck, draw box #5 without a shadow
Frame 8, 40, 10, 73, 4, 11, 0, 11, 0, 32, 0, ""
LOCATE 9, 43: PRINT "Box #5 doesn't have a Shadow!"

'What the heck, draw a frame around it all
Frame 1, 1, 25, 80, 2, 15, 0, 0, 0, 32, 0, _
"Box #6 Frames Without Clearing"

COLOR 7, 0 'Restore color before ending
A$ = INPUT$(1) 'Give a keystroke to end this test.
END 'We're out of here!

[EDITOR'S NOTE]
Due to the formatting of the FRAME subprogram, it is not printed
here. You can find the sub program in the file FRAME.BAS







The QBNews Page 41
Volume 1, Number 3 May 22, 1990



----------------------------------------------------------------------
I n p u t P a s t E n d
----------------------------------------------------------------------

Due to demand, you can now get the QBNews mailed to you on disk for a
nominal charge. The rates are as follows:

United States $3.00

North America (outside US) $4.00

Outside US $6.00

Please add $1.00 if you want it on 3.5" media. Also, you can receive
it in either ZIP, PAK, or LHZ format. If you don't specify, you will
receive it in ZIP format.

The first time you request the QBNews, you will receive all available
issues. That way you won't miss any.

And of course, you can always download it free from Treasure Island.
First time callers are granted limited download privlidges so you can
get it. Treasure Island is 9600 HST, is at FIDO address 1:141/730, and
its phone is 203-791-8532. To request your copy on disk, send US funds
to:

The QBNews
P.O. Box 507
Sandy Hook, CT 06482

Remember to specify which archive format you want.
























The QBNews Page 42
Volume 1, Number 3 May 22, 1990

WE NEED AUTHORS!

If you are interested in writing for the QBNews, you can contact
me at the address below. I can also be reached on Compuserve as
76510,1725 or on Prodigy as HSRW18A. If you are submitting articles, I
ask that they be ASCII text with no more than 70 characters per line.
As far as reviews go, I am pretty well set so what I really want is
code.


You can write me at:

The QBNews
P.O. Box 507
Sandy Hook, CT 06482

David Cleary






































The QBNews Page 43

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT