THE ASSEMBLY LANGUAGE "MAGAZINE" #3 1989
THE ASSEMBLY LANGUAGE "MAGAZINE" VOL 1 NUMBER 3
May, 1989
## #### #### ####### ## ## ###### #### ## ##
#### ## ## ## ## ## # ### ### ## ## ## ## ##
## ## ### ### ## # ####### ## ## ## ## ##
## ## ### ### #### ####### ##### ## ####
###### ### ### ## # ## # ## ## ## ## # ##
## ## ## ## ## ## ## # ## ## ## ## ## ## ##
## ## #### #### ####### ## ## ###### ####### ####
#### ## ## ## #### ## ## ## #### #######
## #### ### ## ## ## ## ## #### ## ## ## #
## ## ## #### ## ## ## ## ## ## ## ## #
## ## ## ## #### ## ## ## ## ## ## ####
## # ###### ## ### ## ### ## ## ###### ## ### ## #
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## #
####### ## ## ## ## ##### ###### ## ## ##### #######
Written by and for assembly language programmers.
Table of Contents
Table of Contents. . . . . . . . . . . . . . . . . . . . . . 2
Editorial. . . . . . . . . . . . . . . . . . . . . . . . . . 3
GUIDE LINES FOR CONTRIBUTORS . . . . . . . . . . . . . . . . 4
Beginners'Corner . . . . . . . . . . . . . . . . . . . . . . 5
Segmentation . . . . . . . . . . . . . . . . . . . . . . . 5
Keyboard driven TSR programs.. . . . . . . . . . . . . . . . 7
Hex Conversion Routines . . . . . . . . . . . . . . . . . . 12
Book Reviews. . . . . . . . . . . . . . . . . . . . . . . . 14
Source Code for Keyboard TSR. . . . . . . . . . . . . . . . 15
Source for Soft Breakout. . . . . . . . . . . . . . . . . . 18
;page 2
Editorial
This is the third issue of the Magazine, and the first with a
major article from an outside contributor. Our thanks to Garrett
Nievin.
It is amazing how few programmers really appreciate
the benefits of assembly language programming. Most of them under-
stand that for certain parts of programs it can increase performance,
but they have no real conception of the amount of improvement that it
can make.
As a common example of this-- The case of sprite
manipulation. Some of the more speed conscious programmers are
aware that to do clean writes to the CGA screen you need to wait
for the retrace periods, but a C programmer cannot chase the
electron beam around the screen to update it in areas that will
not be affected until it again goes over it. This multiplies the
time available to manipulate areas of the screen without flicker.
Much of the time in executing a higher level routine is
spent in calling it and returning from it. The indexed with of-
fset stack operations are very costly in time. This is why even
speedup assembly routines in high level code don't show the full
capabilities as they are still called using the same conventions.
One of the most touted buzz words today is "structured".
This translates into using small easily controlled and understood
subroutines with a single entry point and a single exit point
having sharply limited function. This makes for a program that is
quick and easy to write (regardless of the language) and quick to
debug. These are all valuable attributes in a program, and if
done right these same routines can be included in a large variety
of dissimilar programs.
There are those who say that any programmer who does not
keep to "structured" programming is a bad programmer. At least in
assembly language programming there are times and reasons for
throwing this whole concept away. In doing so you create a
program that is almost impossible to modify, totally unreadable,
insane to debug, and a nightmare to try and document----BUT----you
can also come close to cutting your size (already tiny compared to
anything else) in half and have a good chance of a thirty percent
increase in speed if you do it right. A program like that is not
the product of a "bad" programmer, but of a very dedicated one.
In such a program you don't have the time to waste making a lot of
CALLS or doing variable storage or register adjustments. The num-
ber of items that you must keep track of, mostly in your head,
while writing makes it a project for only the best of programmers.
I gave it up years ago, but still envy those who are able to make
use of a powerful tool.
;page 3
GUIDE LINES FOR CONTRIBUTORS AND 'ADVERTISERS'
Name and address must be included with all articles and
files. Executable file size and percent of assembly code (when
available) should be included when a program is mentioned and is re-
quired from an author or publisher. Any article of interest to
Assembly language programmers will be considered for inclusion.
Quality of writing will not be a factor, but I reserve the right to
try and correct spelling errors and minor mistakes in grammar, and to
remove sections.
Non-exclusive copyright must be given. No monetary compensa-
tion will be made.
Outlines of projects that might be undertaken jointly are
welcome. For example: One person who is capable with hardware needs
support from a user friendly programmer and a math whiz.
Advertisements as such are not acceptable. Authors and
publishers wishing to contribute reviews of their own products will be
considered and included as space and time permit. These must include
executable file size, percent of assembly code and time comparisons.
Your editor would like information on math libraries, and
reviews of such.
Articles must be submitted in pclone readable format or sent
E-mail.
Money: Your editor has none. Therefore no compensation can
be made for articles included. Subscription fees obviously don't ex-
ist. Publication costs I expect to be nil (NUL). Small contributions
will be accepted to support the BBS where back issues are available as
well as files and programs mentioned in articles(if PD or Shareware
ONLY).
Shareware-- Many of the programs mentioned in the "Magazine"
are Shareware. Most of the readers are prospective authors of programs
that can be successfully marketed as Shareware. If you make significant
use of these programs the author is entitled to his registration fee or
donation. Please help Shareware to continue to be a viable marketing
method for all of us by urging everyone to register and by helping to
distribute quality programs.
;page 4
Beginners'Corner
Segmentation
Segments are largely a carry over from the days when
processors were all limited to a 16 bit address buss, but we still
live with them today. They are not without some benefits however.
The way they are treated in most of the books I am familiar with
can leave the novice fear struck over the complexities and this is
not necessary. They are nothing more ( to the assembly program-
mer) than a tool that can either be used or discarded almost at
will.
The 8088 has 4 segment registers; CS, DS, ES, and SS. The
complete address of a memory reference is the sum of the segment
register (shifted 1 hex place to the left) and another register.
Thus each location in memory can be accessed by many (up to 4096)
combinations of segment and offset. For example segment 1000h,
offset 1000 (generally written as 1000:1000) can also be addressed
as 1001:0ff0, as the sum of both of these add up to 11000h. Each
of the segment registers has another register that is hardware as-
sociated with it but that can sometimes be overridden in the in-
struction. The IP (instruction pointer) is firmly attached to the
CS segment. The SP (stack pointer) always points to a location in
the SS (stack segment). DI refers mainly to a location in ES
as SI does to DS.
In standard programming parlance there is the code
segment, the stack segment, and the data segment written in
various ways such as code_seg. High level language compilers
insist on this division and MASM forces you to at least define
them, but you should keep in mind that they are only conveniences
and should be discarded when they cease to be convenient.
In the following discussion I want to make a distinction
between two types of data. The first is data that the program
contains or uses as intermediate storage. This would include
variables addressed in the code, messages to the user, and data
buffers that operated on. The second type is external to the
program. An example of this is a text file that your program will
edit. Let me call the first type program data and the second type
external data. The dividing line is very loose and often ceases
to exist but it is a convenient fiction for now.
The one unchangeable fact that we have to deal with is the
way DOS loads the programs. There are two formats it can
follow: the .COM format and the .EXE format. The COM format loads
quickly because it is simply an image of the machine code and
needs no processing by DOS in order to execute. It is limited in
size to 64k, but I have never seen an assembly language program
that was that large so it is really not much of a limit. All
segment registers are set to the same value on loading (this
value is the address of the PSP) and the IP
is set at 100h.
The EXE format is not limited to size but it requires post
processing by DOS in order to work. This makes it slower to begin
execution. This is also the format that is compatible with the
source level debuggers. During linking the linker assigns segment
offset values to each of the segments you define in your code.
After loading DOS adds this offset value to each location in your
;page 5
code that addresses a segment. This information is contained in a
relocation table that the linker prepends to the file. Then DOS
sets the CS register to the start of your code segment and the SS
register to the start of your stack segment, and DS and ES to the
start of the PSP. The IP is set to the offset specified in the END
statement as the start of execution.
Using either format you are still left with a mess to
attend to. Your program data area is not defined or addressed,
your stack is out of control (if .COM), and your program owns the entire
address space in the computer from the start of your program to
the end of memory. The first responsibility of any program is to
clean this up. The methods of doing this are very simple and can
be s standard header to all of your programs. I won't go into them
now, but will try to cover them next issue. For now let's assume
that you have attended to that and your whole program with code,
stack and program data occupy a few thousand bytes above the
PSP,and you have returned everything else to DOS. This is the
ideal starting point for any program. Whatever other memory you
need for external data can be requested from DOS and it knows how
much is there and unused which your program doesn't. It will
return to you the segment address of the block you requested. This
then you can subdivide into as many segments as you like. For
example: you are keeping a database of 10,000 names. Each name
could be considered to be 2 segments long (32 characters) so you
could move from name to name by adding 2 to the segment register
you are using to address this space. Doing it this way frees you
from the 64k limit you would have if instead you used one of the
index registers and added 32 to it.
This is a complex subject to try and explain, especially
in a short column. More techniques will be discussed next issue.
;page 6
A fool rambles about Keyboard driven TSR programs.
by
Garrett P. Nievin 4518 Valley Brook Dr. San Antonio, TX 78238
o Introduction
First of all, let me describe exactly how a keyboard driven TSR
SHOULD work, to be a friendly inhabitant of your system. To me,
there are two kinds of such programs: those which modify keyboard
functioning (as in the case of Superkey), and those which merely
check for keystrokes to activate a resident function (such as
Sidekick). I will focus my discussion on the latter, but the
basic principles apply to any keyboard TSR.
o Narrative of how a friendly TSR handles the keyboard.
To reliably and safely monitor the keyboard for a keypress, a
program must install an interrupt handler for interrupt 9, which
is hardware generated every time a key is pressed or released.
For now let's assume we have a routine in memory and is the active
interrupt 9 handler. Every time a key is pressed or released, our
program automagically wakes up. The only affected registers are
CS:IP, which point to where our program is executing of course.
Our program should IN a byte from the keyboard port; this is the
scancode which we have been summoned to service. Since the TSR
program is only checking for a certain keyboard condition (such as
an Alt-Q being hit), all other conditions should be ignored. If
it is not "our" key, then we want to do nothing more. To relin-
quish control, we do a long JMP to the old interrupt 9 handler
(which may be BIOS ROM, or it may be another TSR. In this friend-
ly manner, any number of TSR's may happily coexist). If it IS our
key, we perform whatever action is appropriate, and terminate by
one of two methods: jump to the old int handler, which allows the
rest of the TSR's and/or BIOS to get at the same keypress; or,
terminate the interrupt ourselves. This second method involves 3
basic steps: 1) Tell the keyboard we have serviced his keypress
2) Tell the hardware interrupt controller we have serviced the
hardware interrupt to completion and 3) perform and IRET (return
from interrupt) instruction to continue with system processing.
o How to read the keyboard
The keyboard is driven by an 8048 chip, which is tied logically to
the 8259A Peripheral Interrupt Controller (as level 1; only the
timer interrupt at level 0 has more priority) and to the port A
and port B of the 8255A-5 Programmable Peripheral Interface chip.
I only give you these numbers to impress people with; that, and to
get a little better understanding of what all happens. The scan
code of a keypress gets placed in port A of the 8255, which is
mapped to I/O port 60h of the CPU. All keypress scancodes will be
in the range 01 (Escape) to 53h (Delete). If the high bit is set
on, then it is not a keypress scancode, but a key release scan-
code. These are usually ignored, but must be processed non-
etheless.
;page 7
Port A can be safely IN'd from any number of times before the
keyboard is cleared; this is done by getting port B (IN AL,61h),
set the hi bit on (OR AL,80h) and outing it back to port B. Once
the keyboard is cleared, you usually want to tell the 8259 PIC
that the interrupt you've been servicing has been completed by
outing 20h to port 20h. The 8259 prioritizes all interrupts, and
this command tells it that the highest priority interrupt is
finished; this is neat because all the interrupt handlers don't
have to know what level interrupt they are.
Now comes the obvious question: How are we going to check for 2
keys, as in the case of say Alt Q? BIOS makes this one easy. In
the BIOS/DOS data area (segment 40), at bytes 17h and 18h, we find
information on which special keys are being pressed. The sig-
nificant bits are:
17h xxxxXXXX 18h XXXXxxxx 1 Alt is being pressed 1 Insert
is being pressed 1 Ctrl 1 Caps Lock 1 Left shift 1 Num
Lock 1 Right shift 1 Scroll Lock
All we do is wait for the scancode of Q to come up. Then we check
the appropriate bit in the appropriate flag byte (in the case of
alt, we would test 0040:0017h for 08h). If the bit is on, we do
our thing. Otherwise we just pass control on to the next in-
terrupt handler in the string with our long JMP. NOTE: always
use a "normal" key as a "wakeup call"; that is, don't check for
combos like ALT-Rshift. There are too many programs already that
do this, and programs employing these keys are difficult to chan-
ge, thereby ensuring incompatibilities. In fact, if you dis-
tribute your program and don't offer some means of "hot key" con-
figuration, you may want to tell your users where your scan codes
are located in the program so they may patch them themselves to
avoid incompatibilities. Hint: use a CMP and not a TEST to check
this byte, or if you are looking for ctrl-A then ctrl-alt-A,
ctrl-shift-A, etc. will all mistakenly trigger your interrupt.
o How to install the TSR
The installation program will consist of 3 parts really: the jump
around the interrupt handler code to the install code, and the in-
terrupt and install code areas. The install code should do basi-
cally two things: install the interrupt handler using service 25h
of interrupt 21h (DOS call), and of course
terminate and-stay-resident. To do the install, load AX with 2509h
(AL has the interrupt number) and DS:DX with the address of the
interrupt handler code, and do an int 21h. Your program is RIGHT
NOW the new interrupt 9 handler. Then, load AX with 3100h and do
an int 21h again (Terminate and stay res and return an err code of
00 to parent). At this point the handler is working, in memory,
and control is released back to the parent program (in most cases
Command.com).
Here is our opportunity to start getting fancy. To keep memory
usage to a minimum, we should do a DOS service 4Ah to release all
unneeded memory.
;page 8
/* UPDATE: I don't know what I was thinking here, but
that's wrong. You don't use service 4Ah in a TSR; it
would do nothing. What you DO do is load the number of
paragraphs you need to keep into the DX register when you do
your service 31h call. The way I get that is to use:
((last_address-first_address)/16)+1 (to div by 16 just shift
right 4 bits) And don't forget to allow room for your PSP! */
To REALLY cut down on the memory, we can relocate our program
backwards into the unused PSP area, saving something like 164
bytes. Also, we can leave a memory signature so that the user is
not allowed to install the TSR more than once. My usual method of
this is pretty simplistic. I put 4 unique bytes into the TSR it-
self, and then upon installation check if those bytes are there.
If so, the installation aborts and the user is told. The flaw
here is that it only prevents the same program being installed
after itself. If such a TSR is loaded and takes int 9, another
one is later loaded and takes int 9 also, the program could then
be installed for the second time. I know how my system is con-
figured, so this is no problem. To a novice user getting hold of
one of my programs, this will at best just waste a little memory.
At worst, the system can get a little weird. To more securely
guard against re-installation, you may take another interrupt vec-
tor (there are 256, after all, and only a couple dozen are used in
a basic PC). The way that works is: search for an empty in-
terrupt vector (one which has all zeros), and take one when you
find it. Save a memory signature there. When the program in-
stalls the TSR code, search the interrupt vectors for your memory
signature. If you find it, you know your program is there al-
ready. Much safer. (Aside: For my personal use, I combine all
my TSR's into one program. This saves memory, time, and trouble.
It also keeps me from installing TSR's in between one another!)
o What you can get away with in a TSR
If you have read this far, I assume you don't already know all
this. So, I will not go into all the undocumented MS-DOS services
for TSR writers. If you are interested in them, I suggest you
check back issues of magazines like Programmer's Journal and Dr.
Dobb's. Anyway, you are restricted as to what can happen inside a
TSR. It all boils down to one thing: MS-DOS is not reentrant.
It is not meant to be multitasking (under programs like DoubleDos,
there are copies of DOS for each task running), and so a routine
can not be called while it is already executing for somebody else.
Here is a scenario: You are saving a document from your word
processor, and during the wait call up a TSR to find a phone num-
ber or something. The TSR does a disk read of your phone # file.
The TSR finishes and exits back to the word processor. Now, the
WP's disk write has been interrupted, losing vital information
like where on the disk he was writing, etc. MS-DOS goes awry. It
is time for the big red switch, and kiss your document adios.
Hope your disk is not messed up as well.
There is the why of all this, now here is the what. A TSR may not
use any DOS int 21h services higher than 0Ch. It is restricted to
console and printer I/O services. (Now, this is of course not
TOTALLY true, but how to do disk I/O etc. are beyond the scope of
this little rambling. Maybe some other time.)
;page 9
/* UPDATE Okay, here's how to do anything you please from
within a TSR, including disk I/O. The BEST way is to install
interrupt "front-end" handlers for int 13h (BIOS disk I/O),
int 21h (DOS), int 25h (absolute disk read), int 26h (absolute
disk write), and I think that's all. In these front-end hand-
lers, set a flag (or semaphore or TS byte or whatever you want
to call it) whenever anyone is in the middle of a disk I/O.
Then, in your TSR, don't do a disk I/O if any of these flags
are set. You will also need to steal the user timer tick in-
terrupt (1Ch) to periodically check for when those interrupts
are free for use. Or, you can use the cheater way (which is
used by DOS itself): Whenever DOS is waiting for a keypress,
it calls int 28h (undocumented). The only program I know of
"legally" using this is the PRINT command. Whenever int 28h
is called, you should be free to do whatever you want. If you
want more immediate access to the disk, though, you'll need to
use the timer int (1Ch) again. ONCE, at initialization time,
do an int 21h ah=34h. In ES:BX, you will have the address of
a byte called INDOS by most folks. This is an undocumented
function which has been officially declared off-limits by
Microsoft, but nobody pays attention to them anyway. As far
as I know, it works fine in all versions of MS-DOS as of today
(April 1988). In your timer interrupt servicer, read the byte
at the address you got from service 34h. If it's not zero,
don't do anything over service 0ch; if it's zero, go crazy.
What this byte is (I'm told) is a count of how many calls deep
DOS is into itself. The actual value is of no relevance to
us, just if it is zero or not. Now, I don't see this helping
much if the program is doing BIOS I/O, but I don't use any
software I know of which uses BIOS for disk I/O. Also, if the
application is doing direct-to-the-hardware disk I/O, I don't
think you probably want to be messing with this stuff. Again,
I don't use anything that I know of doing this.
I've written several programs using the int 28h/service 34h combo
to do TSR disk I/O and have never had a single problem, and I
tested them in the middle of all kinds of disk accesses. This
does not mean it's always cool, though. If you can help it, al-
ways avoid undocumented stuff. In my opinion, Microsoft won't
change it because so much software relies on it now, but I've
learned not to trust them.
o Example program complete with flimsy excuses
The Sperry PC has a high-res graphics screen which is treated as a
logically different display from the normal text screen, and uses
different display memory banks. The text bank, called screen A,
can be superimposed over the graphics (called screen B) on the
screen simultaneously. This program switches between screen A on-
ly, screen B only, and screen A superimposed on screen B. This
program is for example only, and you should not attempt to run it
on a CGA. If something gets fried, it's not my fault. It looks
for a keypress of Alt-Esc, goes into action, and then quits.
I hope this has been helped you understand how a basic keyboard
driven TSR works. If you have any more questions, feel free to
contact me with them, and I will find you an answer. Most ad-
vanced TSR functions (almost all are undocumented by Microsoft and
;page 10
IBM!) have been discussed in Byte, PC Tech Journal, and Dr.
Dobb's Journal, but have been thoroughly discussed in Programmer's
Journal. I highly recommend this publication, and hope more
people will subscribe, so it does not fold up and deprive me of
all the information it provides.
Call the Telstar BBS: (512)822-8882 for a good time
Garrett P. Nievin has also contributed a piece of code to service a
soft breakout switch for Symdeb and Debug. Please see the listing in
the source code section. Both of these are extremely well commented
and should be quite useful.
;page 11
Hex Conversion Routines
Just a little "quicky" to fill out this issue. These are a
couple of routines that I use regularly in a variety of ways. They are
both fast and fairly tightly coded and they work. I hope you find them
of some use.
The first, DECHEX, takes an ASCII number pointed to by SI and
returns a hex number in AX. The other just reverses the process, and
when used with the little MOV_ASCII puts it where you tell it to.
;~
DECHEX: ;THIS ROUTINE WILL TAKE A [CL] (MAX 5) DIGIT
;ASCII DECIMAL NUMBER POINTED TO BY SI AND
;RETURN A 4 DIGIT HEX NUMBER IN AX.*
XOR DH,DH
XOR AX,AX
TRY: CMP CL,1
JBE LASTA
MOV DL,[SI]
SUB DL,"0"
ADD AX,DX
MOV BX,AX
SHL AX,1
SHL AX,1
SHL AX,1
ADD AX,BX
ADD AX,BX
INC SI
LOOP TRY
LASTA: MOV DL,[SI]
SUB DX,"0"
ADD AX,DX
RET
HEXDEC: ;THIS ROUTINE WILL TAKE A 4DIGIT HEXADECIMAL NUMBER IN AX AND
;RETURNS A 5 DIGIT ASCII IN BH,BL,DH,DL,AL.
;TIME AVERAGES ABOUT 140 CLOCKS PER DIGIT
;LEADING 0'S ARE SUPRESSED
MOV BX,0
MOV CX,0
MOV DX,0
A10K: CMP AX,10000
JB A1K
SUB AX,10000
INC BH
JMP A10K
A1K: CMP AX,1000
JB HUNDREDS
SUB AX,1000
INC BL
JMP A1K
HUNDREDS: CMP AX,100
JB TENS
SUB AX,100
INC DH
JMP HUNDREDS
;page 12
TENS: CMP AX,10
JB UNITS
SUB AX,10
INC DL
JMP TENS
UNITS:
MOV AH,'0'
ADD BH,AH
ADD BL,AH
ADD DH,AH
ADD DL,AH
ADD AL,AH
CMP BH,AH
JNZ UEND
MOV BH,020H
U1:
CMP BL,AH
JNZ UEND
CMP BH,020H
JNZ UEND
MOV BL,020H
CMP DH,AH
JNZ UEND
CMP BL,020
JNZ UEND
MOV DH,020H
CMP DL,AH
JNZ UEND
CMP DH,020
JNZ UEND
MOV DL,020H
UEND:
RET
MOV_ASCII: ;THIS TAKES THE DIGITS PRODUCED BY
;HEXDEC AND PUTS THEM AT DS:SI
MOV [SI],BH
INC SI
MOV [SI],BL
INC SI
MOV [SI],DH
INC SI
MOV [SI],DL
INC SI
MOV [SI],AL
RET
;~
;page 13
Book Reviews
The Programmers' Reference
I made a quick review of this last month, but I have since
gone over it thoroughly and received the extended package from the
author. This is NOT an instruction book for beginners. It is
just what it names itself- a reference manual.
The shareware version is 10 chapters (about 600k bytes) of
solid information and a table of contents. Much of the information
is very difficult to find elsewhere. The first chapter is a quick
history of DOS from 1.0 to 4.0. Chapter 2 has the Port addresses
and the Interrupts up to 0fh. Chapter 3 has the true Bios
interrupts 10h to 1fh and it throws in INT 20h for some strange
reason. This isn't just a listing that you might find in many
places, but a detailed description with calling conventions and
special things to watch out for. It includes semi-documented
functions such as all of the Desqview and Topview int patches that
go into this area. Each interrupt lists what machines it is
included in from Tandy to PCjr to PS-2 model 80. I haven't counted
them, but there must be thousands.
The DOS interrupts 20h through 4fh are covered in similar
detail, with many notes and application information.
Further chapters contain information on file structures,
EMS memory, and many other topics.
The registered version gives another disk full of
miscellaneous data that is rare and useful.
The $15 price is one of the greatest bargains to be found.
Available on many BBS's as ####ref.??? such as 1029ref.zip. This
says that it is the October 29th release, and the extension is up
to the Sysop.
;page 14
Source Code for Keyboard TSR
; High Res screen A/B toggler for Sperry PC, the best PC around
; Written for MASM 4.0
code segment para public 'code'
assume cs:code,ds:code,es:code
org 0100h ; .COM format
start proc far
; Equates - ports, locations, values
; Using equates makes the program much easier to change later;
; to use a different scancode here, for example, you would just
; change the equate for "esc".
if1 ; only assemble equates on pass 1
portB equ 61h ; 8259 port B - used to clear scancode
keyport equ 60h ; 8259 port A - keyboard scan codes
esc equ 01h ; scan code for Escape key
hibiton equ 80h ; hi bit on for keybreak scan code & reset
hibitof equ 7fh ; hi bit off for keybd reset
DOS_area equ 40h ; segment of BIOS/DOS data area
shftstat equ 17h ; keybd shft status flag in DOS area
shift equ 00000011b ; value for shftstat meaning either shift is on
eoi equ 20h ; end of interrupt flag
comdport equ 20h ; 8259 command port
endif
begcode:
jmp implant ; jump around interrupt handler code
begres: ; area to remain core resident
rescode proc ; keystroke causes jump to resident code here
jmp oversign ; jump over memory signature
db 'abGN' ; program signature in memory
; I use my initials in uppercase and a 2-byte program ID in lowercase
; Variables
mode db 0 ; superimpose mode: 0-A,1-B,2-Both,3-None
oldint9 dw 0,0 ; doubleword value of old int9 vector
oversign: ; start of code for new interrupt 09h handler
push ax ; save that register
push ds ; save DS.
mov ax,DOS_area ; DOS data segment
mov ds,ax ; now covered by DS
test byte ptr ds:[shftstat],shift ; is a shift being held now?
jz normal ; nope, not a hot key, pass it on to old int
in al,keyport ; yes, get keyboard scan code
cmp al,esc+hibiton ; was it release of escape?
je abnorm ; yes, lose that scan code
cmp al,esc ; was it an escape?
jne normal ; nope, skip the important stuff
;
mov al,mode ; get mode
cmp al,2h ; both screens on?
;page 15
je aonly ; yes, go back to first
add al,1h ; add 1 to hi nybble to go B to A to AB
jmp nextmode ; and go put it back
aonly: ; start back with screen A only
xor al,al ; turn off
nextmode: ; put in new mode and quit
mov mode,al ; move in new mode
mov ah,15h ; set superimpose mode function of int 10h
int 10h ; and go change mode
jmp abnorm ; lose scan code
;
abnorm: ; abnormal handling of scan code. to the bit bucket with it.
in al,portB ; get portB
or al,hibiton ; set acknowledge / clear keyboard bit
out portB,al ; and out it again
and al,hibitof ; clear ack bit
out portB,al ; out that, enabling keyboard again
cli ; no interruptions please, Mrs. a-Whiggins
mov al,eoi ; end-of-interrupt command
out comdport,al ; send it to the 8259
pop ds ; restore
pop ax ; registers
sti ; interruptable again
iret ; quit without performing normal interrupt
normal: ; not hot key, pass scan code on to normal (maybe) int9
pop ds ; restore all
pop ax ; registers I messed with
jmp dword ptr cs:oldint9 ; goto old int9 handler
; all code from here out is based on Norton's guide, and I use it in
; all my TSR's.
reslen equ endres - begres ; length of new resident code
strtres equ begres - begcode + 100h ; start of resident code
psplen equ 5ch ; length of necessary PSP
implant: ; code to put new int 9 front end in core
mov ax,3509h ; get int vector function
int 21h ; get vector of interrupt 9 handler
cmp es:[bx+3],'ba' ; am I already loaded?
jne fresh ; nope, go install
cmp es:[bx+5],'NG' ; make sure I'm not already in
jne fresh ; naaah, go install
lea dx,cs:stalemsg ; DX points to already installed message
mov ah,09h ; DOS display string func
int 21h ; go display message
int 20h ; and quit
fresh:
lea dx,cs:instlmsg ; DX points to installation message
mov ah,09h ; display string function
int 21h ; give the user the poop
mov ax,3509h ; get int vector function
int 21h ; get vector of old interrupt 9 handler
mov oldint9,bx ; int location in ES:BX, save it
;page 16
mov oldint9+2,es ; "
mov ax,2509h ; set new interrupt 9 vector to me
mov dx,offset rescode ; address of which is in DX
int 21h ; go do it
push cs ; DS and ES both end
pop es ; up pointing at
push cs ; code area
pop ds ; "
mov di,psplen ; where program will end up
mov si,strtres ; start of resident code
mov cx,reslen ; amount of resident code to move
cld ; go forward in move
rep movsb ; move code back in mem
mov dx,psplen + reslen ; DX pointing to next free paragraph
mov ax,3100h ; keep, return code of 0
int 21h ; terminate
stalemsg db 'Screen A/B toggle is already installed and active!',13,10,7,7
instlmsg db 'High Res Screen A/B toggle',13,10
db 'Shift/Escape toggles screen A only, then B only, then A/B.'
db 13,10,'$'
start endp
code ends
end start
;page 17
Source for Soft Breakout
; BUGOUT.COM
; Program to add wimpy "soft" breakout key to DEBUG and SYMDEB
;
; MASM compatible source code
;
; Hitting the '5' on keypad forces an int 3 (debug breakpoint)
; After breakpoint, add 1 to IP register and trace through IRET
;
; By Garrett P. Nievin
;
; (BTW, to add a hardware breakout switch just put a switch between
; pins 17 (NMI) and 40 (+5V) on your 8088 chip, and a switch between
; pins 21 (RESET) and 40 will do a system reset/reboot.)
code segment para public 'code'
assume cs:code,ds:nothing,es:nothing
org 0100h
hotkey equ 4ch ; scan code for hot key = "5" on keypad
portA equ 60h ; 8255A port A, usually keybd scan code
portB equ 61h ; 8255A port B, various switches
release equ 80h ; hi bit in scan code means key release
hibiton equ 80h ; hi bit in byte for or'ing in
hbitoff equ 7fh ; hi bit in byte for and'ing out
start proc far
begcode:
jmp implant ; jump around interrupt handler code
begres: ; area to remain core resident
rescode proc ; keystroke causes jump to here
jmp over ; jump over memory signature
db 'bcGN' ; program signature in memory
over: ;
sti ; allow interrupts
push ax ; save that register
in al,portA ; get keyboard scan code
cmp al,hotkey ; hotkey?
jne normal ; yes, go hanlit
push dx ; save register
mov dx,0020h ; port 20h = PIC port to send EOI to
mov al,dl ; 20h turns on EOI bit
out dx,al ; tell interrupt controller we be done
pop dx ; restore register
in al,portB ; get portB
or al,hibiton ; set acknowledge / clear keyboard bit
out portB,al ; and out it again
and al,hbitoff ; clear ack bit
out portB,al ; out that, enabling keyboard again
pop ax ; restore original register
int 3 ;
iret ;
;page 18
normal: ;
pop ax ; restore that register
jmp dword ptr cs:oldint9 ; goto old int9 handler
oldint9 dw 0,0 ; doubleword value of old int9 vector
rescode endp
endres: ; end of core res area
reslen equ endres - begres ; length of new resident code
strtres equ begres - begcode + 100h ; start of resident code
psplen equ 5ch ; length of necessary PSP
implant: ; code to put new int 9 front end in core
mov ax,3509h ; get int vector function
int 21h ; get vector of interrupt 9 handler
cmp es:[bx+3],'cb' ; already loaded?
jne fresh ; nope, go install
cmp es:[bx+5],'NG' ; make sure
jne fresh ; naaah, go install
lea dx,cs:stalemsg ; DX points to already installed message
mov ah,09h ; DOS display string func
int 21h ; go display message
mov ax,4cffh ; terminate with code = 0xff
int 21h ;
fresh:
lea dx,cs:instlmsg ; DX points to installation message
mov ah,09h ; display string function
int 21h ; give the user the poop
mov ax,3509h ; get int vector function
int 21h ; get vector of old interrupt 9 handler
mov oldint9,bx ; int location in ES:BX, save it
mov oldint9+2,es ; "
mov ax,2509h ; set new interrupt 9 vector to me
mov dx,offset rescode ; address of which is in DX
int 21h ; go do it
push cs ; DS and ES both end
pop es ; up pointing at
push cs ; code area
pop ds ; "
mov di,psplen ; where program will end up
mov si,strtres ; start of resident code
mov cx,reslen ; amount of resident code to move
cld ; go forward in move
rep movsb ; move code back in mem
mov dx,psplen + reslen ; DX pointing to next free paragraph
mov ax,3100h ; keep, return code of 0
int 21h ; terminate
stalemsg db 'Bugout already resident.',10,13,7,'$'
instlmsg db 'Bugout loaded and active, keypad-5 is break key.',10,13,'$'
start endp
code ends
end start
;page 19
Comments
Post a Comment