Skip to main content
© Photon Storm (Ilkke & Rich)

THE "ATARI ST VIRUSKILLER" PROGRAMMING TRICKS (AND TIPS)
by Richard Karsmakers

Over the last three years, I have been programming my "Atari ST
Virus Killer" (previously known under the name "Virus Destruction
Utility"). In those years, the program has evolved from a little
thing to check disks for the "Signum" virus into a fairly
extensive and very effective tool in the battle against viruses.
I have learned programming properly in that time, too (well...
not all too properly, but who cares about that?), and therefore I
would like to share some of the source code of my virus killer
with you, hoping to give you some interesting tips & tricks of
how to program in GfA Basic.
All examples quoted here are from GfA Basic 3, though adaptation
to GfA Basic 2 shouldn't prove to be too difficult (but, then
again, it may).

----------------- CHECK HOW MUCH MEMORY IS LEFT -----------------

Some of you may think this is done with the help of the FRE(0)
command of GfA. Nothing is less true, however! FRE(0) supplies
you with the amount of bytes free for programming in GfA Basic
itself. Once the program has been compiled, and you want to
really see what amount of memory is available without actually
wanting to get the figure of free Basic work space, you can use
the following Gemdos call:

a%=(GEMDOS(72,L:-1))

The amount of memory that's truly left will afterwards be in a%
(where else had you expected it?!).

------- CHECKING WHETHER OR NOT A DISK IS WRITE-PROTECTED -------

It is very difficult to find out whether a disk in drive A or B
is write-protected or not. The Operating System does not offer
any functions for this, and therefore people often resort to
using 'illegal' (not officially documented) system variables that
are likely to change with every TOS version.
I used to do exactly the same, and so did Stefan (hence the
reason why ST NEWS often didn't work on STE computers - and we're
not even talking about the TT!).
When I was in Norway last winter, Lord HackBear gave me a better
routine. It accessed the FC (Floppy Controller) directly, and
seemed to work smoothly. Yet it didn't in special cases. So I had
my colleague Michael Bittner at Thalion software look it over. He
made it better, but it turned out not to work with two disk
drives attached (not properly, at least).
So, finally, another colleague by the name of Chris Jungen
solved the problem and hence I can now offer you a routine that
definitely works on all ST systems - although I have not tested
it on the TT and it may not work there...

FUNCTION wrpr(crd%)
SDPOKE &H43E,-1 !Floppy operations off
~XBIOS(29,NOT (2*(crd%+1))) !Select drive
SDPOKE &HFF8606,&H80 !FDC-statusregister select
buf%=DPEEK(&HFF8604) !FDC-statusregister read
~XBIOS(30,2*(crd%+1)) !Deselect drive
SDPOKE &H43E,0 !Enable floppy operations
buf%=(buf% AND 64)/64 !Isolate WP-bit
RETURN buf% !Return WP bit as function value
ENDFUNC

The function can be used as follows, where a% contains the
device number (0=A, 1=B):

IF @wrpr(a%)=1
ALERT 1,"DISK WRITE PROTECTED!",1,"SHIT",b%
ELSE
ALERT 1,"EVERYTHING OK!",1,"YEAH",b%
ENDIF

---------------------- WAITING FOR A KEY ------------------------

In most programs, it is often required for the program to just
wait. Wait until a key is pressed, or until a mouse fire button
is pressed, or either. The following routine does this in quite a
perfect, fool-proof way:

PROCEDURE waitkey
LOCAL taste$
LPOKE XBIOS(14,1)+6,0 !Clear keyboard buffer
taste$=""
DO
taste$=INKEY$
EXIT IF MOUSEK OR taste$<>""
LOOP
RETURN

Since the routine only has to wait, nothing is done with the
result. This result, however, is located in the variable 'taste$'
so you can do whatever you want with it. If, however, you want to
use the result after getting back from this subroutine, you have
to get rid of the line that reads 'LOCAL taste$'.

--------------------- INITIALISING A DRIVE ----------------------

When scanning through a drive or partition (or, worse, through
several different floppy disks), it is possible that one of the
following things happens:
A) The system still 'thinks' it's got the old disk in the drive.
B) A Gemdos 'file not found' or 'out of memory' error occurs.
These are both 'bugs' in the Operating System, and I am lead to
believe that at least B) has been discarded in TOS 1.6 (possibly
even 1.4). The way to solve this is reading the directory off a
disk. GEM know recognises that a new disk is there, and all
internal buffers are empties like they should.
However, you have to initialise the proper drive - and you
don't want any stupid filenames all over the screen, do you?
So what you have to do is put the drive number in 'devno%' (A=0,
etc.), and then use:

buf$=CHR$(65+devno%)+":SHIT.QXY"

The somewhat unusual search template makes sure that no
filenames occur on the screen.

----------------------- FORMATTING A DISK -----------------------

The option to format a disk is very useful to include in a
program that writes files to disk; the user must be able to
quickly format a disk without having to leave the program if he
doesn't have some readily formatted disks handy.
The following is the routine that is implemented in the "Atari
ST Virus Killer". Is formats a disk single-sided and has been
optimised to do just that. Flexibility is gone, and since I was
too lazy to make it flexible again (sorry, folx!), I just added
some comments here and there. Nothing more.

PROCEDURE format
LOCAL e%,t%,d% !Something to do
ALERT 2,"ARE YOU SURE YOU|WANT TO FORMAT|FLOPPY IN
DRIVE A|SINGLE SIDED?!",2,"YES|NO",d%
IF d%=1 !Yeah....format!
screen$=SPACE$(10000) !Reserve space for format buffer
e%=0 !Error variable
t%=0 !First track to format
DO
SHOWM !See note #1
' See note #2
e%=XBIOS(10,L:V:screen$,L:0,0,9,t%,0,1,L:&H87654321,0)
IF e% !Error occurred!
ALERT 1,"FORMAT ERROR!!",1,"AHEM",d%
EXIT IF e%
ENDIF
INC t% !No error....increment track!
EXIT IF t%>79 !All tracks formatted
LOOP
IF e%=0 !DO-LOOP left without error
' This installs the BPB in the bootsector
' Check contents of BPB in any good ST book ("ST Intern")
screen$=STRING$(6,0)+MKL$(XBIOS(17))+CHR$(0)+MKI$(2)+CHR$(2)
screen$=screen$+MKI$(&H100)+CHR$(2)+CHR$(112)+CHR$(0)
screen$=screen$+CHR$(208)+CHR$(2)+CHR$(&HF9)
screen$=screen$+MKI$(1280)+MKI$(&H900)+MKI$(256)
screen$=screen$+MKI$(0)+STRING$(512,0)
SHOWM !See Note #1
' This writes the bootsector
VOID XBIOS(9,L:V:screen$,L:0,0,1,0,0,1)
VOID BIOS(7,0) !Get BPB
' Default (empty) FAT contents
screen$=MKL$(&HF9FFFF00)+STRING$(508,0)
SHOWM !See Note #1
' Write both FATs
VOID BIOS(4,1,L:V:screen$,1,1,0)
VOID BIOS(4,1,L:V:screen$,1,6,0)
ALERT 1,"THIS DISK NOW HAS|"+STR$(DFREE(1))+" BYTES
FREE!",1,"OK",d%
ENDIF
ENDIF
RETURN

Note #1

Gemdos has the tendency to disable the mouse whenever doing disk
I/O. Should an error occur during that disk I/O that causes a GEM
alert box to be displayed, you will then find it very hard to
select "OK" or "Cancel". This solution seems to work.

Note #2

This is the actual Xbios format command. The parameters are the
following:

e%=XBIOS(10,L:V:screen$,L:0,0,9,t%,0,1,L:&H87654321,0)
| | | | | | | | | |
| | | | | | | | | Virgin word
| | | | | | | | Magic longword
| | | | | | | Interleave
| | | | | | Side (here, only side 0)
| | | | | Track number (here, 0-79)
| | | | Sectors per track
| | | Device (here, only A)
| | Filler (any value...doesn't matter)
| The pointer to the format buffer
Xbios function call #10

The 'virgin word' is the word with which the track will be
filled after formatting. The 'magic longword' is necessary to
make the routine format at all. The 'interleave' is the sequence
in which the sectors are written on a track (different
interleaves with different numbers of sectors per track result in
different data transfer speeds).

--------- SCANNING A DRIVE/PARTITION FOR ALL ITS FILES ----------

This routine is the heart of the linkvirus scan routine, and
originally written by Stefan. There were a couple of bugs and,
let's say, 'unwanted skips', in the routine, so therefore I did
some additional coding on it. Except for that bit of 'further
coding', it's a straight port of a harddisk backup program that
Stefan wrote an article about aeons ago (in ST NEWS, of course).

PROCEDURE do_backup
buf$=CHR$(65+devno%)+":SHIT.YXQ" !See above
FILES buf$ ! " "
LOCAL e$,x$,fold%,fspec$,x%,curr_path$,currdrive%
'
curr_path$=DIR$(0) !Store current path
IF curr_path$="" !If nothing
curr_path$="\" !Root
ENDIF
currdrive%=GEMDOS(&H19)+1 !Current drive
fold$(0)=RIGHT$(path$,LEN(path$)-2) !Get first folder name
CHDRIVE ASC(LEFT$(path$))-64 !Change to it
fold%=0
e$=SPACE$(13) !File name buffer
'
CLS
PRINT "Now checking drive..."
PRINT "Press Escape to abort!"
REPEAT
CHDIR fold$(fold%) !First folder...and more
x%=LEN(DIR$(0))+3
PRINT AT(2,7);DIR$(0);"\" !Print first bit of name
'
DEC fold% !Dec folder
fspec$=nomo$+"."+ext$ !Search template
SHOWM !See above (Note #1)
e%=GEMDOS(&H4E,L:V:fspec$,-1) !Gemdos Sfirst
WHILE e%=0 !No error...
' The following checks if filename is FOLDER or VOLUME
IF (PEEK(GEMDOS(&H2F)+21) AND 16)=0 AND
(PEEK(GEMDOS(&H2F)+21) AND 8)=0
x$=SPACE$(13) !Clear name
BMOVE GEMDOS(&H2F)+30,V:x$,13 !DTA address
PRINT AT(x%,7);e$ !Print name
PRINT AT(x%,7);x$;SPACE$(45) !And more...
' Here, you can DO something with the file if you want
' Insert your own routines!
ENDIF
BMOVE V:e$,GEMDOS(&H2F)+30,13 !Get from DTA
SHOWM
e%=GEMDOS(&H4F) !Gemdos Snext
IF ASC(INKEY$)=27 !Escape pressed?
EDIT !Yes....quit!
ENDIF
WEND
'
fspec$="*.*"+CHR$(0) !Search template
SHOWM
e%=GEMDOS(&H4E,L:V:fspec$,-1) !Sfirst
WHILE e%=0
IF (PEEK(GEMDOS(&H2F)+21) AND 16)
x$=SPACE$(13)
BMOVE GEMDOS(&H2F)+30,V:x$,13
IF x$<>"."+CHR$(0)+SPACE$(11) AND x$<>".."
+CHR$(0)+SPACE$(10) !Special folder files?
INC fold%
fold$(fold%)=DIR$(0)+"\"+x$
ENDIF
ENDIF
BMOVE V:e$,GEMDOS(&H2F)+30,13
SHOWM
e%=GEMDOS(&H4F) !Snext
IF ASC(INKEY$)=27
EDIT
ENDIF
WEND
UNTIL fol<0
CHDRIVE currdrive% !Set back old drive
CHDIR curr_path$ !Set back old dir
RETURN

---------------------- COOKIE JAR HANDLING ----------------------

On TOS 1.6 and up, Atari implemented something called 'The
Cookie Jar'. This is a reserved area in memory where certain
values represent certain things. It was included for the STE and
TT's sakes, so that program would have ways of getting to know
which hardware they were addressing.
Of course, Atari totally neglected to document this feature, but
after many hours of searching and months of experiencing (and
glancing over an issue of German "ST Magazine") I can now offer
you some info about it.

At address $5A0 (an official system variable that will not be
changed any more - but then again you never know with Atari),
there will be a zero or a longword pointer.
If you have a zero there, this means that:
A) Someone shoved a magazine into your system.
B) You have a TOS version lower than 1.6, or the pre-version of
TOS 1.6.
If you find a longword pointer, this will be the address to find
the Cookie Jar.
The Cookie Jar is, strictly taken, a collection of 32-bit
values, of which two belong together each time. The first one is
usually a value representing some kind of ASCII string, and the
second is the actual value. Usually, this is another address.
ASCII strings starting with an "_" are off Atari and should never
be used by other people rather than Atari!
The last Cookie is always a longword zero followed by the
maximum number of cookies that can be used (only a specific
amount of memory is reserved for Cookies, and you should not use
more unless you want to install it all yourself again and expand
it - but that's too much to tell here).
That's basically all there is to know about the Cookie Jar. Its
use is simple: Apart from other programs just getting parameters
from it, it can also specify addresses of variable locations that
can then be used by other programs.

Some of the officially documented Atari Cookies are the
following. First follows the longword ASCII string, and then the
value(s):

_CPU This is not hard to imagine. It specifies which
processor is present in the system. It can be 0, 10, 20
or 30 for 68000, 68010, 68020 and 68030 respectively.
_VDO Version number of the Videohardware. This is actually
represented by two words. The first word is the spot in
front of the command; the second the one after.
0,0 ST
1,0 STE
2.0 TT
_SND A bit table that tells programs which sound
possibilities they have. Only two of these 32 bits are
used - but let's hope that this shall not last long
(idle hopes...).
Bit 0 YM soundchip (ST and STE)
Bit 1 Stereo DMA sound (STE)
_MCH Described the machine you use. Here, also, we're
talking about two words.
0,0 260 ST, 520 ST(F)(M), 1040 ST(F)(M)
1,0 MEGA ST (Difference: Real Time Clock)
2,0 STE
3,0 TT
_SWI Value of configuration switches, if present. Don't ask
me what this is supposed to mean. I don't know (neither
do any mortals outside of Atari Corp., I suspect).
_FRB Since the TT's "Fast RAM" is not usable for DMA
transfers, the BIOS of that computer creates a 64 Kb
buffer area in memory of which the address can be found
here.

Well...that's all there is to say, really. Now, let's head for
the source bit.

PROCEDURE cookie
LOCAL x%
CLS
IF LPEEK(&H5A0)<>0 !Cookie jar present!
x%=LPEEK(&H5A0) !Cookie jar address
DO
' This prints the hex cookie address, the cookie
identification and the
' cookie value after each other
PRINT HEX$(x%);" -- ";MKL$(LPEEK(x%));" -- ";
HEX$(LPEEK(x%+4))
ADD x%,8
EXIT IF LPEEK(x%)=0
LOOP
ELSE
PRINT "No Cookie Jar present (yet)!"
ENDIF
RETURN

---------------- CREATING A CLEARER FILESELECTOR ----------------

In TOS versions lower than 1.4, the fileselector didn't allow
any optional parameters to be supplied (like "SELECT FILE TO
LOAD"). In TOS 1.4 and up, using GfA Basic 3, you can specify
this using an optional string parameter of the FILESELECT
command.

FILESELECT "FILE TO LOAD","\*.*","MYFILE.FIL",lo$

If you belong to the enormous groups of people 'blessed' with
lower TOS versions, you can use the following routine (medium and
high res only). It just puts a bit above the fileselector box
before it is called. This may be at the wrong place for certain
alternative fileselector boxes!
It is being called as follows:

@fileselectmooi("FILE TO LOAD")
FILESELECT "\*.*","",lo$

And this is the routine:

PROCEDURE fileselectmooi(a$)
DEFFILL 0,1
IF XBIOS(4)=1 !Medium res
PBOX 0,0,319,40
BOX 0,0,320,26
BOX 0,3,319,23
BOX 1,4,318,22
DEFFILL 1,1
PBOX 2,5,317,21
GRAPHMODE 3
DEFTEXT ,,,6
TEXT 25,18,a$
ELSE !Other res (here: High only!)
PBOX 157,20,482,60
BOX 157,20,482,54
BOX 160,23,479,51
BOX 161,24,478,50
DEFFILL 1,1
PBOX 162,25,477,49
GRAPHMODE 3
DEFTEXT ,,,13
TEXT 184,43,a$
ENDIF
GRAPHMODE 1
RETURN

-------------- CHECKING WHICH DRIVES ARE CONNECTED --------------

This routine has obvious uses. The only problem is that I
haven't actually found ways of really checking for disk B. The
system seems to think it's always present.
Tough shit.
Another problem is that I don't really grasp my own code any
more. All I know is that, in the end, you get a variable called
att$ in which you'll get e.g. "A|B|C|D|Cancel". Further, in ndr%
you'll find the actual number of drives attached.
I know you may find this a bit shit, but I am simply too lazy to
find out what I've done again (it may not even work all on its
own when not surrounded by the rest of the virus killer program).
Just regard it as some kind of extra. It's always better to have
something than nothing.

PROCEDURE drijfbitz
' * Get drive bits and determine which drives are attached
'
ad%=&H4C2 !Drivebits system variable
ac$=BIN$(LPEEK(ad%)) !Get active drives
buf%=LEN(ac$)
IF buf%>2 !RAM-and/or harddisks attached
norm!=FALSE
ELSE !Only drive A or A+B attached
norm!=TRUE
ENDIF
'
ERASE dr%()
DIM dr%(buf%) !Dim an array for that
y%=0
x%=0
DO
IF MID$(ac$,buf%-x%,1)="1" !Drive present?
IF x%>1 !Harddisk only!
SHOWM
IF BIOS(7,x%)>0 !Get bpb address, if>0, drive
is REALLY present
dr%(y%)=65+x% !Put letter in array
INC y% !Next array element
ENDIF
ELSE
dr%(y%)=65+x% !Put letter in array
INC y% !Next array element
ENDIF
ENDIF
INC x% !Next bin$ element
EXIT IF x%>buf%
LOOP
att$=""
x%=0
DO
EXIT IF dr%(x%)=0
att$=att$+CHR$(dr%(x%))+"|"
INC x%
LOOP
att$=att$+"Cancel"
ndr%=x%-1 !Number of drives attached minus one
RETURN

-------------------- OPENING ALL THE BORDERS --------------------

Well, well. I suppose you're dying to know this, aren't you? OK.
It's very simple. You load in an assembler and use the source
featured in ST NEWS Volume 5 Issue 1. Hack a bit...assemble...
execute...and you're there!
Alas, this is slightly difficult to do in GfA Basic. If someone
knows how to do it properly (full screen, that is) in 100% GfA
Basic (maybe some sync scrolling on top of that), he can call me
and get my ST system for free.

--------------------- REVERSE TEXT DISPLAY ----------------------

To make a certain word or line of text stand out among the rest,
it can be useful to reverse its display mode - i.e. the text
becomes white and the block around the characters becomes black.
You can use the following routines to do this:

PROCEDURE on !Reverse on
PRINT CHR$(27);"p";
RETURN
PROCEDURE off !Reverse off
IF XBIOS(4)=2
PRINT CHR$(27);"q";
ELSE
PRINT CHR$(27);"q"
ENDIF
RETURN

---------- DISPLAYING AN ASCII TEXT FILE ON THE SCREEN ----------

In the special toolkit version of the "Atari ST Virus Killer"
that I usually drag around myself, I also implemented a routine
that displays an ASCII text file on screen. The skeleton of this
routine stems from Stefan - who really is much smarter than me.

PROCEDURE text_on_screen
CLS
LOCAL lo$,a%
PAUSE 10 !See note #1
FILESELECT DIR$(0)+"\*.*","",lo$
IF lo$<>"" AND RIGHT$(lo$)<>"\"
OPEN "I",#1,lo$
WHILE NOT EOF(#1)
LINE INPUT #1,a$
PRINT a$
IF INP?(2)
a%=INP(2)
IF a%=32
@waitkey !Use the above routine
ENDIF
IF a%=27
EXIT IF TRUE
ENDIF
ENDIF
WEND
IF a%<>27
@waitkey !Use the above routine
ENDIF
ENDIF
CLOSE #1
RETURN

Note #1:

When using alert boxes or item selectors in a program, it is
very likely that the user still keeps the fire button of the
mouse pressed when they occur. If the user would have to select
to 'load a file' with the mouse and the item selector would
appear instantly, the chances are big that a file will be
selected in that item selector that the user didn't want.
Hence a small pause to make sure that the mouse fire button is
released.

--------------------------- A SIGNAL ----------------------------

Sometimes, it can be really useful to give the user a signal of
some kind. For optimal effect, this needs to be audio-visual,
i.e. a bleep and a bit of screen flashing. To most of you, this
will seem very simple to do. The screen flashing is a bit more
difficult than you may think, though - if you want to do it
extremely legally, that is.

PROCEDURE scr
LOCAL buf%
buf%=XBIOS(7,0,-1) !Get value of color #0
buf%=(buf% AND &HFFFF) !Isolate proper word
a%=0 !Initiate counter
WHILE a%<6 !Not yet six times?
IF XBIOS(4)=2 !High res
SETCOLOR 0,&H101 !Invert
PAUSE 2 !Wait a bit
SETCOLOR 0,&H100 !Normal again
ELSE !Low res
SETCOLOR 0,&H777 !White
PAUSE 2 !Wait a bit
SETCOLOR 0,0 !Black
ENDIF
INC a% !Next time
PAUSE 2 !Wait a bit
PRINT CHR$(7); !Bell sound!!
WEND
SETCOLOR 0,buf% !Old color #0 back
RETURN

These were the tips and tricks for this issue. Bye for now!

Disclaimer
The text of the articles is identical to the originals like they appeared in old ST NEWS issues. Please take into consideration that the author(s) was (were) a lot younger and less responsible back then. So bad jokes, bad English, youthful arrogance, insults, bravura, over-crediting and tastelessness should be taken with at least a grain of salt. Any contact and/or payment information, as well as deadlines/release dates of any kind should be regarded as outdated. Due to the fact that these pages are not actually contained in an Atari executable here, references to scroll texts, featured demo screens and hidden articles may also be irrelevant.