AN EXAMPLE OF CONVERTING A PROGRAM WRITTEN FOR MASM INTO ONE WRITTEN FOR A86
by Eric Isaacson
Recently a user sent me the file SETUP.ASM, a program written to assemble
under Microsoft's MASM assembler. He wanted to know why A86 wouldn't assemble
it, and why he should choose A86 over MASM, in view of the fact that there are
hundreds of programs out there written for MASM. This paper is a response to
that question. In it, I show the minimum alterations necessary to produce an
A86 source file that assembles to an equivalent functioning program. I go on
from there, to show how the language extension features of A86 can let you
make the program simpler and clearer. Finally, I do an extensive reworking of
the program, rewriting portions of code, renaming variables and procedures,
and adding more comments. By carefully studying the source program at each
stage of the transformation process, you'll learn not only about the
differences between A86 and MASM, but about how to transform an ordinary
assembly-language program into a well-written one.
STEP 1. CONVERTING THE PROGRAM
The program that the user sent me is in the file SETUP.ASM. To make a COM
program file from this source file using MASM, you must assemble it with MASM,
link the resulting OBJ file using LINK (ignoring the 1 error message that LINK
always gives you when you use MASM to create a COM file), feed the resulting
EXE file to EXE2BIN, and finally rename the resulting BIN file to SETUP.COM.
The resulting file is 3129 bytes long.
If you attempt to assemble SETUP.ASM using A86 version 3.04, you'll get four
errors. The first three are all of the same type-- an attempt to MOV a symbol
into a general register, where the symbol is a segment name defined with a
SEGMENT AT directive. I corrected this by replacing the SEGMENT AT directives
(all three are together at the top of the program) with EQU directives,
EQUated to the AT-value. Similarly, the single symbol defined within each of
the SEGMENTS was converted to a definition via EQU.
The fourth error is the END BEGIN at the end of the program. Since A86 treats
this as equivalent to MAIN EQU BEGIN, and since the program already has a
symbol MAIN defined elsewhere, this was a multiple definition. I corrected
this simply by deleting the END BEGIN line, since A86 doesn't need the END
These modifications took me five minutes to find and make, and I had a program
that A86 accepted. But the program didn't work. A glance at the code
contents revealed that interrupt drivers were involved, in which the DS, ES,
and SS registers didn't have the same values as the CS register. Some of the
memory accesses needed explicit CS override bytes coded for them, since A86
doesn't act on ASSUME directives. I scanned through the code and found four
places where CS overrides were needed. Diagnosing and solving this problem
took about twenty minutes; so I had a working A86 program within a half hour.
I would guess this to be worse than average, since most assembly-language COM
programs don't use multiple-segments the way interrupt drivers do.
*** I have made enhancements so that starting with Version 3.05, my assembler
will assemble SETUP.ASM without modification. I added interpretation of
SEGMENT AT to be an EQU of the segment name to the AT value, followed by
entry into STRUC mode for assembly of declarations within the structure.
V3.05 also completely ignores the END directive in non-OBJ mode.
The source for the minimal changes I have described is the file S1.8. The
command A86 S1.8 makes an S1.COM file directly, that is functionally
equivalent to the SETUP.COM file produced by MASM, LINK, EXE2BIN, and REN.
The S1.COM file is 3105 bytes long, which is 24 bytes shorter than SETUP.COM.
The difference comes from 19 unnecessary segment-override bytes produced by
MASM, plus 5 LEA instructions coded in situations where an equivalent MOV
instruction would have been more efficient. A86 automatically generates the
more efficient MOV instruction, and MASM doesn't.
STEP 2. INTRODUCING A86 LANGUAGE FEATURES THAT DON'T CHANGE THE PROGRAM
Of course, I could have stopped with step 1. But I wanted to explore the
possibilities for improving the program, so I continued. The next step was to
add A86 language features, to make the program clearer. The first part of
this transformation was to eliminate all unnecessary red-tape directives, and
to replace all the place-marker symbols with A86 local labels (L0 through L9).
The symbols were easy to find, since the author of the program followed the
reasonable MASM practice of ending place-marker symbols with digits. Because
the local names are only two characters long, it becomes reasonable to indent
instructions by only two spaces, leaving room for longer comments. Next, I
found places to incorporate A86 language features into individual instruction
sequences: multiple operands to PUSH POP INC DEC, conditional returns, MOV
segreg,segreg, and IF cond stmt. The resulting program, S2.8, produces a
virtually identical 3105-byte COM file to S1.8, but the source code appearance
is considerably changed for the better.
STEP 3. MAKING MINOR CODE OPTIMIZATIONS
Next, I started making changes to improve the code itself. Among the changes:
1. I renamed many variables and procedures, to make the program easier to
follow. Same of the renaming was simple unabbreviation: for example,
VIDEO2MEM to VIDEO_TO_MEM, and PAGE_NO to PAGE_NUMBER. Other renaming
involved making a completely different, clearer choice of name: for
example, SETUP_STATUS to WINDOW_ACTIVE?, and MAIN to OUR_HANDLER.
2. I combined that three original SEGMENT ATs and the variables they contained
into three DD constants, to be loaded into pointers with LDS or LES
instructions, followed by indirect memory instructions to access the
3. I rearranged the main interactive loop to make the flow of control clearer.
The exit-code executed when the ESC key is pressed is separated from the
main flow, and clearly labelled ESC_SEEN. The rest of the main loop was
consolidated and arranged to clearly show its loop structure.
4. I performed code optimizations based on 86 instruction set features the
original author didn't take advantage of. For example, the sequence
was replaced by a simple REP MOVSW instruction. The preceding CLD
instruction was moved to the start of interrupt processing, reducing the
procedure WRITELN to the single REP MOVSW line. So I eliminated WRITELN
and replaced the instances of CALL WRITELN with REP MOVSW. I could have
treated MEM2MEM similarly, except that MEM2MEM is never called so it can be
eliminated outright. Finally, the sequence
was replaced by MOV AL,DISPLAY_TABLE[BX-2] .
In addition to the coding changes, I converted all non-comment code to all-
caps, to complete the conversion of the program to my formatting conventions.
The resulting program, S3.8, assembles to a 3065-byte COM file, 40 bytes less
than the previous version due to the optimizations.
STEP 4. MAKING MAJOR CODE OPTIMIZATIONS
For the final revision of the program I made major code optimizations:
1. I completely overhauled the usage of memory variables. Some variables,
such as ATTRIBUTE1, were eliminated, their values kept in registers. The
tables VIDEO and DISPLAY_TABLE were replaced by the more directly useful
ENABLE_VALUE and VIDEO_SEG.
2. I redesigned the parameterization of VIDEO_TO_MEM and MEM_TO_VIDEO, that
defines the size and location of the pop-up window. It had previously been
a run-time parameterization, always called with the same values. I
introduced a set of EQUs for the parameters, defining the window at
assembly time. VIDEO_OFFSET was transformed from a run-time procedure to
an assembly-time expression EQUate. All constant references depending on
window size were recast in terms of the parameter-symbols. Assertion-
checking was also introduced to insure that the declaration of the window
contents contains the proper number of bytes.
3. The program was rearranged to save space. The buffer used for storing the
contents of the screen beneath the pop-up window was moved to the start of
the program, so that the 256-byte PSP buffer could be a part of the storage
buffer. The initialization code and window source code was moved there
also, to save space. The buffer containing the constructed window image
was moved to beyond the area of the COM file, reducing the size of the COM
file by 1064 bytes in one stroke.
4. The array CODE_TABLE was converted from fixed-length records to delimited
variable-length records, saving more program space.
5. The pushing of all registers except AX was deferred until after the hot-key
check, to reduce keystroke computational overhead time when the program is
not in effect.
6. More changes were made to improve program readability. The installation
code was broken up into procedures, for clarity. The keycode values FUNC
and SHIFT_F were declared and used, to make the key-scanning code easier to
follow. The program itself was renamed to the more descriptive EPSONSET,
and an explanatory paragraph was added to the top of the source code. The
instruction-level comments were improved.
The resulting final source program is S4.8, which assembles to a COM file of
only 1237 bytes, less than 40% of the original program size. I retained the
original copyright notice, although it might be argued that I transformed the
program enough to call it my own.