2657 lines
108 KiB
Plaintext
2657 lines
108 KiB
Plaintext
|
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
|
|||
|
|