home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Collection of Hack-Phreak Scene Programs
/
cleanhpvac.zip
/
cleanhpvac
/
ASMVEG4.ZIP
/
ASMVEG4.TXT
next >
Wrap
Text File
|
1996-12-23
|
15KB
|
282 lines
Assembly Language for Veggies (And C programmers) Part 4.
Welcome Back... As I commenced to write part 4, I did what I usually do which
is to re-read over part three.... and hece I found a MISTAKE! <Gasp!>
In the section that tells you all about what uses the first 64k of RAM, I said
the area between 0000 and 0100 was used by the interrupt table vectors... now
if one takes 256 and multiplies it by 4 bytes per interrupt one gets 1024.
1024 in HEX is of course 400 - so the area 0000 - 03FF is ALL used to store
the vectors of the interrupts.... My cincere aplologies for those misled, but
hey, bugs creep into anything!!
OK... on to Edition 4.. What will we look at? <Sits here, makes cup of
coffee, searches a few ASM books, spills coffee, crashes machine, chucks on
the Midnight Oil, hassles users then....> thinks of one largely unknown and
little looked-at area of PC Programming...... the TSR.
TSR's are without a doubt the wierdest pieces of software! They rely on bugs,
twists and turns, undoccumented parts of DOS and most of all, they perform
HIDDEN MULTITASKING!!!
I'll highlight the situation by taking a simple program as an example...
When I'm doing work on my PC, I find myself constantly flipping in and out of
DOS, doing this, that and the other, and soon I begin to lose track of the
time.. I can't be stuffed putting a battery in the clock on the wall (Besides
it never was accurate!) so waaaaaaay back when I was learning ASM I decided to
write a non-interactive TSR that would, as long as I was sitting at the DOS
prompt, display the current time in the top right hand corner of the screen.
The clock was required to vanish when an application was run (to avoid
cluttered screens) and to automaticly re-appear at the sign of the DOS prompt.
CC (Clock in Corner) is the result. CC's source and COM should be inside the
archive that comes with ASMVEG4.
Now, some general stuff on how TSR's work.
A TSR is just like any other COM program EXCEPT for a few rules.. As you
know, when a COM program gets loaded, it gets handed all available free RAM,
up to the 640k mark for it's own use.. Same for a TSR... in fact at load
time, DOS has no idea if a program will be a TSR or not. Now, a TSR does a few
things just a little differently that a normal COM program... In a normal COM
program, wellyou just begin executing whatever it was the program was writren
for.. a TSR instead has to run an initialization routine that installs
itself into RAM and makes sure DOS knows that that RAM will be in use from then
on. The initialization routine should also set up all the other bits pertinant
to that particular TSR (IE setting up it's hotkey, or looking for the COMports
or whatever..) Finally, the TSR must discard all uneeded RAM and quit to DOS.
Because of this loader arrangement the TSR should be coded thus:
--------- program start (CS:0100 at load time) -----
JMP INITIALIZATION ; go to the initialize routine
---- TSR CODE
... etc
---- TSR CODE
INITIALIZATION:
--- init routine
... etc
--- init routine
----------- Program END -----------
The initialization routine makes use of a DOS call that lets you quit, yet
leave some RAM marked as Used, so that DOS will leave it alone and move it's
internal referances to where the bottom most free memory location is up to
past the end of your TSR.
This function call is called "Terminate & Stay Resident" (No joke) and is
number 031h. (Dos 2.0+ required).
One places the ammount of memory to reserve (in paragraphs) into the DX
register then calls INT 021h as per any normal DOS function call. <note: A
paragraph is 16 bytes>
A typical sequence of actions for an initialization routine would be to
display a program title screen, set any required interrupt vectors (see next
section), calculate the number of bytes to be left in memory (rounded up, of
course!) and then call function 031.
The reason for the initialization routine bieng right at the very END of the
code is that you can tell DOS to only keep memory up to the end of the TSR
code, but BEFORE the initialization code. This means that DOS will discard
your initialization code (which can be as big as you like) and only keep the
TSR bit. These days it pays to keep as little in memory as possible.
So we now know how to tell DOS to keep our code in memory without destroying
it, but our code will just sit there and do nothing... We have to make sure
that our code is regularly executed to do it's work... for this we have
several methods...
As you recall all DOS & BIOS functions are run via INTERRUPTS, and interrupts
are REDIRECTABLE... SO al we have to do is redirect a few interrupts to our
code and off we go.. Perhaps an example will clarify here: Let's say we code
a TSR to come to life upon hitting of the ALT-F10 key.. All keyboard presses
cause an interrupt, so all we do is redirect the interrupt to ourselves, and
bam... every time a key is hit, out routine gets called... our routine should
replace the BIOS one (easy!) - and should read they key, see if it's ALT-F10,
if so, clear the KB controller and jump to our routine.. if it's not for us,
it should pass back to the original routine (the BIOS one) without clearling
the key from the keyboard buffer (so that the BIOS can re-read & process it).
Simple as that! CC doesn't use the KB, so you may ask how it runs? Simple
also.. There's an interrupt called the "Idle Loop" interrupt just made for us.
The idle interrupt is called by DOS whict DOS is busy doing nothing... The
idle loop by default normally passes control straight back to DOS, but if we
direct it to ourselves, we basicly get control handed to us WHEN DOS IS NOT
BUSY DOING OTHER THINGS.
One time DOS is not busy is when it's waiting for user input at the C:\>
prompt... (it is not busy at other times too, but it's a good start).
There's another flag in low memory that indicates for sure if DOS is on the
commandline or not, but CC doesn't bother checking this (could be done later
to improve the program - I leave this to you to try and work out on your OWN..
You need to research the InDos FLAG - Answers in a future ASMVEG!!)
The other thing that all TSR's must watch is that they don't try and run under
themselves - take for example if CC was in the middle of displaying the time
and another Idle Interrupt occurred? DISASTER!! so CC has a flag it sets the
first time it's called.... next time round it checks the flag and if it's set,
it quits back to DOS because it knows it's already going... if the flag is
clear it knows it's OK to go to work... needless to say the last thing CC
does after displaying the time is to clear the flag again.
CC's initialization routine gets the current screen mode, and if it's mode 7
assumes a MONO screen... if it's not 7 it assumes colour... CC also assumes
an 80 colum mode (hey, I said it was simple!!) and sets a pointer into video
memory accordingly..
CC then does the work of getting and setting interrupt vectors... CC is rather
rude in that it assumes it's the first Idle interrupt user and won't pass on
down the chain (Again, I said it was simple!!). Try to add a pass-on routine
if you're feeling extravigant.. you'd have to use the GETintVector function,
then save that in a variable addressable in the TSR module, then instead of
doing an IRET, you'd have to do a direct JMP to the old address... why not try
this as a real challenge of your knowlegdge.. again, the improved version will
appear in a later ASMVEG to show you the way that I did it...
If you don't follow that, take this: assume another program is already making
useif the idle interrupt... CC always does an IRET after bieng called... what
it should do if jump to wherever the old idle interrupt pointed to so that if
there is another program making use of the interrupt, it too will get called.
Take this for example:
IDLE INT ---> old TSR ---> original idle int ---> Back to Caller
CC Currently:
IDLE INT ---> CC ---> Back to Caller (original idle int & old TSR are no
longer called - nasty!)
CC Should do:
IDLE INT ---> CC ---> old TSR ---> old idle int ---> Back to Caller
This way everything behaves.... the simple short term fix is to load CC first,
but what if you get 2 programs that both misbehave in this regard? OUCH!
CC then works out how big CC is and goes TSR. just to further balls things up,
CC uses an older way of going TSR that basicly works the same as function 031
but instead runs off INT 027. INT 027 is DOS 1.0 compatible (as this
originally ran under DOS 1.1!!!!). Another option would be to update to the
more compatible 031 function call... again, I leave this to you to experiment
with...
The TSR Part of CC....
The TSR part of CC begins at the GET_TIME label.. first off, we save ALL
registers.. this is essential so that the main program that was interrupted
doesn't get screwed up by us...
CC then checks to see if it's already running and quits if it is... If CC
isn't already running, it sets the flag to say that it now is, gets the time,
saves it in the HOURS, MINUTES, SECONDS variables and gets the screen buffer
address. Next it displays the time, works out the AM/PM stuff (which isn't
perfect - see where I went wrong if you can...remember, the hour between
midnight and 01:00 is AM, the time between noon and 01:00 is pm...). Lastly,
it clears it's busy flag, restores the registers and returns to the main
system. You might say that CC is naughty in directly accessing the video RAM,
but it does it for 2 reasons: The main one is SPEED... if you take too long,
then you'll slow things down by having al these idle interrupts
bottlenecking... the other reason is often not understood...
YOU MUST NEVER CALL A DOS FUNCTION FROM INSIDE A TSR!! NEVER!!! DOS is
not written to be "Re-Entrant". What this means is this: Say DOS is part way
thru something or other (OK this probably doesn't apply for CC, but a hotkeyed
TSR might certainly have this problem) for argumnets sake say DOS is 1/2 way
thru a disk write. If we go calling another DOS routine it'll upset all of
DOS's internal pointers and stuff and when the original routine resumes (ie
when our TSR quits) all of DOS's internal data will be screwed up.. on the
other hand most of the BIOS is quite re-entrant (thus the use of BIOS int 02C
to get time)... in fact when you issue a mode change command, INT 010 calls
itself 12 - 16 times (depending on version) to reposition the cursor, set the
colours, reprogram the video controllers, etc...!!!
This could be bypased with caution, referance to the InDos flag (See above)
and the like, but it's certainly not consistent over all versions of DOS, nor
reliable for all function calls. Best not to take the risk I always say...
That's why TSR's often stuff things up... Old sidekick's are notorious for
this! The original sidekick uses the timer tick interrupt to see if another
TSR or program has stolen the keyobard interrupt away from sidekick... if it
has, sidekick steals it right back! the result: you can pop up sidekick, but
your main program suddenly ignores all your keystrokes!!!
Sidekick does this by making use of yet another interrupt - the TIMER TICK...
The BIOS generates an interrupt that excecutes once every 18.2 milliseconds
(read about 55 times a second) NO MATTER WHAT. Again, normally this interrupt
returns straight back to the BIOS but we can tap it and thus our TSR will be
called at a constant non-machine speed dependant rate... a LOT of CPU speed
rating programs use this interrupt to derive a constant speed referance by
which to rate things... other TSR's use it as a constant patch to make sure
their TSR gets called regularly (Stuff like mouse drivers, Fax card software
and even multitaskers use this interrupt!)
A final comment...
CC could be improved even further.... it could make use of INT 010 and get
the video mode current at that second - and adjust itself if it found itself
in 40 or 132 colum mode...
Also, takethis situation: If CC is run again, it will simply go TSR and steal
a bit more memory.. the old copy of CC won't execute any more, but it will
still sit there in memory (Question: WHY? Answer is in text above)
What CC should do in the initialization section is to search RAM for a
pre-existing copy of CC (by doing a <say> 16 byte compare of the first 16
bytes of the copy bieng executed with the first 16 bytes of all possible CS
settings up to (but not including) the current CS.. If no match, no CC
loaded... if a match, CC already resident, so the running copy should abort..
As for uninstalling... this is a major patch... first off, CC should make sure
that the idle interrupt still points to the TSR copy of CC (and hasn't been
pinched - if it has uninstall is impossible) then redirect the idleinterrupt
back to the old, original address (another reason for saving te original
address!!), then you need to call the DOS deallocate memory block program and
point it to CC's TSR area...
Messy, yes, but can be achieved with a little effort...
Most of these mods will appear in a later ASMVEG (read when I get round to
fixing it!!) but for now, print out or read the source code, run the thing and
try it out... I know it doesn't behave 100% with doubledos (tends to pop up at
the wrong time) - under desqview no idea, but probalby needs to be loaded into
a "writes directly to screen - Yes" and "uses own colours - yes" window..
probably won't handle the screen correctly either if you're not in 80x25 mode
at the time, but then again it wasn't written to be a fully blown program...
So what, may you ask, is all this about HIDDEN MULTITASKING that i was on
about before.... consider this: you're at the DOS prompt... you can run
anything at any time, and yet this program constantly displays the right time
onscreen, and DOS is oblivious to it's existance.... two programs are running
at once, yes? is this not multitasking? ... it's not really, rahter a sort of
task swapping where it runs one program for a bit, then another for a bit,
then swaps back, but it does it fast enough to look like true tasking...
doubleDos is based around the exact same principal.. each time a timer tick
interrupt occurrs some base code inside DD hands control over to the other
window, first restoring all that window's flags & variables etc... next time
it swaps to the other window and sets up that window's setup.. it's alittle
more complex than that, espescially with regard to device sharing, but that's
all that basicly forms the multitasking core..... on a 386 it's different,
but for a 286 and 8088/8086 this is the ONLY way to task DOS!!
Perhaps now you follow why it's referred to as hidden - there's no
Multitasking driver, rather just normal DOS and normal BIOS doing things as
they always do, just bieng twisted about to suit us.... now you see why I said
TSR's were sneaky?
f there's interest, I might release some better source for some TSR's
including a pop-up of some description??? I'll have ta see what I can do for
ya's if there's interest...
Ok, well go forth and hack! Until ASMVEG5.... .\\erlin