home *** CD-ROM | disk | FTP | other *** search
- 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