Classic Computer Magazine Archive COMPUTE! ISSUE 77 / OCTOBER 1986 / PAGE 85

PC Mini-Assembler

Georg Zimmer

Are you interested in learning 8088 machine language for the IBM PC? This clever program takes advantage of the system program DEBUG to create a complete, label-based machine language assembler. The program requires BASICA for the PC, as well as the program DEBUG (included with MS-DOS). Owners of PC-compatibles should check the instructions at the end of this article before typing in the program.

8088 machine language—the "native tongue" of the IBM PC and its compatible computers—is both powerful and comparatively easy to program. The 8088 microprocessor offers many high-level instructions—such as string commands, multiplication, and division—that aren't available with simpler processors such as the 6502. Best of all, the PC operating system contains a large number of software interrupts (built-in routines) which are easy to call from machine language. With interrupts, you can do everything from writing a dot on a graphics screen to opening disk files.

The first tool you need for machine language programming is a convenient, reliable assembler which converts an ASCII file of symbolic instructions—usually called the source file—into a file containing object code which the computer can execute directly. "PC Mini-Assembler" is a label-based assembler written entirely in BASIC. Although it's not as powerful as IBM's own assembler or macro assembler, Mini-Assembler provides all the basic features you need to assemble a machine language program on an IBM PC. If you're using a PC-compatible computer, read the special instructions at the end of this article before attempting to use this program.

Getting Started

Type in Program 1 and save it to disk as an ordinary BASIC program. Program 2 is a short assembly language source program which we'll use to demonstrate how PC Mini-Assembler works. Programs 3 and 4 are short INCLUDE files required to assemble Program 2. Use the BASIC editor to type in Programs 2–4. Although these are not BASIC programs, we have listed them with the usual IBM Proof-reader checksums; if you type these programs with a word processor or text editor, do not include the checksums. Programs 2-4 must be stored as ASCII files, not as tokenized BASIC programs. If you enter them from BASIC, save them to disk as ASCII using the, A option of SAVE. For instance, this command saves a file in ASCII form with the filename HEXCONV.ASM:

SAVE "HEXCONV.ASM", A

The filename extension .ASM is a conventional identifier for IBM assembly language source files. You may include this extension for the sake of consistency; however, it is not required for this assembler. You must save Program 3 with the filename STACK.LIB, and save Program 4 with the filename CLS.LIB. Put the source file (Program 2) and the INCLUDE files (Programs 3 and 4) on the disk you will use for the assembly. Before using the assembler, you must also copy the program DEBUG.COM from your DOS disk to the disk that contains the source file.

Once the work disk contains the necessary files, load and run Program 1. The program begins by displaying a directory of all the files on the current disk. Then it asks for the name of the file you wish to assemble. Enter the full filename, including any extension. If the file is not found, the program prints an error message and allows you to reenter the name. Otherwise, the assembly proceeds automatically. Several passes are needed to finish the process, most of which is visible on the screen.

When the assembly is complete, Mini-Assembler prompts you to enter a name for the output file (executable object file). At this point you can choose to create two different types of files. To create a command (.COM) file, include the extension .COM or .com with the filename. A command file can be executed simply by typing its filename from the DOS prompt. If you do omit the .COM extension, Mini-Assembler assumes that you want to create a file which can be called from BASIC, and creates a file appropriate for that use.

Of course, it's impossible to explain all the details of 8088 assembly language programming in a magazine article. I learned about the subject from COMPUTE's Beginner's Guide to Machine Language on the PC and PCjr (available from COMPUTE! Books). Many other good texts are also available.

Pseudo-Ops

An assembly language source file contains two kinds of instructions—opcodes and pseudo-opcodes. What we usually call opcodes are actually mnemonics, descriptive names for the binary codes that comprise the actual machine language instruction. The mnemonic RET, for instance, stands for the opcode that performs a RETurn. The function of an assembler is to convert source file mnemonics into an executable series of opcodes.

A pseudo-opcode is an instruction to the assembler rather than a symbolic name for a machine language instruction. Commercial assemblers such as the IBM Macro Assembler permit you to use many different pseudo-ops. PC Mini-Assembler offers a more limited set of assembler directives. Here's a list of all the pseudo-ops the program recognizes.

Origin. The first line in your source code must indicate the starting address for the program. This function is performed by the asterisk (*) pseudo-op. For a PC with at least 96K, use 1C00H for the segment. An offset of zero is best for files that will be BLOADed, but for .COM files, you should use an offset of 0100H, because that's where DOS loads .COM files. Here are two typical origin directives:

10 * 1C00 : 100; .COM file
10 * 1C00 : 0; BLOAD file

Symbol Definitions. Assembly language programs normally use symbolic names to refer to program variables and labels (addresses within the program). The period (.) pseudo-op tells the assembler that the preceding string is a symbolic label or variable. Symbols may contain spaces. You may have a symbol alone on a line, or an instruction or data may follow it:

10 VIDEO FUNCTION. INT 10
20 TEST LABEL.
30 MOV AX, VARIABLE
40 JMP TEST LABEL
50 VARIABLE. DB "HELLO",0

Number Converter. Mini-Assembler assumes that all numbers are expressed in hexadecimal (base 16) notation. The percentage (%) pseudo-op tells the assembler that the following number is decimal, not hexadecimal. When it assembles the program, Mini-Assembler converts the number to hexadecimal. Here are a few examples:

100 MOV AH, %64
110 SUB AX, %10
300 DB DUP %10 (%20)

Text-To-ASCII Converter. The apostrophe(') pseudo-op changes a single character to its equivalent ASCII code. Do not enclose the character; only one apostrophe is needed:

100 MOV DL, 'A
300 MOV BX, 'A

Comment. The semicolon (;) allows you to add comments to a program. The assembler ignores everything on the line after the semicolon:

10 ;DISK SECURITY PROGRAM
120 MOV CX, %10 ;REPEAT 10d TIMES

Forced Label Assignment. The equal sign (=) pseudo-op allows you to create variables that have addresses outside the program. You must specify which segment override the assembler is to use. You should assign all variables at the beginning of the source code. Do not confuse this pseudo-op with the EQU pseudo-op (see below). EQU and = perform similar functions, but = is only for use with variables whose address is outside your program area:

10 SCREEN = ES : 0
20 STORAGE = DS : 80

Data Byte. The DB pseudo-op is used to put byte values in a program's data section. When entering ASCII characters as data, enclose them in double quotation marks rather than apostrophes:

100 DB "HELLO$"
110 DB "Hello", 0, "how are you", 0
120 DB DUP %10 ("Hello", 0)

Data Word. The DW pseudo-op puts word values in the data section of a program. Numbers are stored in low-byte/high-byte format:

100 DW AB1E, %1000, FFD2
110 DW %10, %20, %30

INCLUDE. The INCLUDE pseudo-op causes the assembler to include a library file from disk as it assembles the main program. INCLUDE files typically contain often-used routines or code segments. Instead of retyping a routine every time you write a new program, you can simply enter it once (using label names that you are not likely to use again), and save it to disk. Library files usually end with the .LIB extension. The example program uses two INCLUDE files: STACK.LIB and CLS.LIB. These files should not contain an origin (*) and must be saved in ASCII format, just like the source file. Do not enclose the INCLUDE filename in quotation marks:

10 INCLUDE STACK.LIB
20 CALL CLS
90 INCLUDE CLS.LIB

EQUate. The EQU pseudo-op equates a value to a constant. The value can be text, a number, or even an instruction:

30 BNE EQU JNZ ;LEGAL
40 VIDEO EQU %16 ;LEGAL

Note that you cannot use a constant within a constant. The following line is illegal because VIDEO is a constant:

50 VIDEO FUNCTION EQU INTO VIDEO

OFFSET. The OFFSET pseudo-op tells the assembler to return the offset (address) of a variable rather than the value contained in the variable:

120 MOV DX,OFFSET MESSAGE
200 MESSAGE. DB"HELLO$"

DUPlicate. The DUP pseudo-op tells the assembler to duplicate a DB or DW directive the number of times specified in parentheses. It is often used to create work space. Be sure to include the % sign for decimal numbers, and enclose all text in quotation marks. The assembler may take a long time to perform a DUP operation that uses a large value (1000H, for instance). Do not use a question mark to signify a value that's unknown at the time of assembly; instead, use a 0:

1000 DB DUP 100 (0);256 bytes
1100 BUFFER. DB DUP %16 ("")
1200 TABLE. DW DUP 3 (1, 2, 4)

Do not try to enclose one DUP within the parentheses of another DUP. For example, the following statement causes an error:

1300 DB DUP 8 (DUP 3(0))

Assembly Tips

Here are a few tips that will help you get the most out of this program. First, you can speed the assembly process by using a disk that contains only the files you need for Mini Assembler. Program 3 (STACK.LIB) can be INCLUDEd whenever you need to set up your own stack space.

Mini Assembler does not support the ASSUME pseudo-op. Instead, it automatically puts all variables in the code segment of the program. Unless you specify a segment with the = pseudo-op, the assembler automatically precludes all memory addressing instructions (those which use a named variable for an operand) with the CS: over-ride.

Many texts on 8088 machine language state that you should define a program as a far procedure by using the PROC FAR pseudo-op (for a far return to DOS or BASIC). As long as the far-return address has been pushed onto the stack, you can do the same thing by using RETF to exit the program.

Because of the way that DEBUG works (see below), there are two significant differences between Mini-Assembler and the IBM assemblers and the IBM assemblers. First, you cannot use an operand after XLAT or any of the string instructions because DEBUG won't accept those constructions. For instance, use XLAT alone instead of XLAT source-table (in this case, source-table is implied). Similarly, use REPE MOVS alone rather than REPE MOVS destination-source (again, destination-source is implied).

Secondly, you cannot use segment overrides in the middle of an instruction. A segment override is actually an instruction in itself , and DEBUG becomes confused when it occurs within another instruction. Thus, use ES:MOV AX,SCREEN instead of MOV AX,ES:SCREEN. With Mini-Assembler, you shouldn't have to worry about segment overrides very often; simply use the = pseudo-op if a variable is outside the program.

Compared to commercial assemblers, Mini-Assembler is exceedingly compact. This is possible because it relies on DEBUG.COM to perform most of the actual work. On the first pass, Mini-Assembler reads the entire source file, replacing labels, constants, and variables with nulls. It creates a work file on disk, pipes this file through DEBUG, and sends DEBUG's output to a second file. Then the program scans the second file, replacing nulls with target addresses. At this point it creates another file, which is piped through DEBUG again. The resulting file is scanned again, and target addresses are changed where necessary. This step is repeated until all the addresses are correct.

Mini-Assembler does not require that you use the LINK program. When it writes the object file to disk, the process is complete. Remember, a file that ends with .COM can be executed from the DOS prompt. But if you save the file with any other extension, you must BLOAD and then CALL it from BASIC. Appendix C of the IBM BASIC manual contains more information about combining machine language with BASIC.

PCjr And PC-Compatibles

Because the PCjr's cartridge BASIC does not support the BASIC SHELL command, you cannot run Mini-Assembler on a PCjr with cartridge BASIC. If you have a PC-compatible MS-DOS computer, you may be able to use Mini-Assembler with little or no modification if your BASIC is compatible with IBM BASIC. DEBUG.COM is an MS-DOS (not an IBM) product, and is supplied with many MS-DOS machines. Keep in mind, however, very few so-called compatible computers are truly compatible with the PC in every way. There are many slight incompatibilities which might prevent this program from working as intended on a non-IBM machine.

Program 1: PC Mini-Assembler

For instructions on entering this listing, please refer to "COMPUTE!'s Guide to Typing In Programs" in this issue of COMPUTE!.

HP 10 REM MINI ASSEMBLER
JJ 20 DIM SC$ (100), NL$ (100), OF$ (100), L$ (25), AD$ (25), GS$ (25)' MAKE LARGER IF NECESSARY
MM 30 KEY OFF : SCREEN 0 : CLS : COLOR 2 : DEFINT A-Z : FILES
ID 40 ON ERROR GOTO 770
NN 50 X = 1 : L = 1 : FI = 1 : INPUT "ENTER SOURCE FILE;rdquo;; F$
IP 60 IF LEN (F$) = 0 THEN PRINT "BYE;" : END
HO 70 OPEN F$ FOR INPUT AS #FI : CLS
DE 80 ON ERROR GOTO 0
KM 90 LINE INPUT #FI, A$ : PRINT A$ : GOSUB 730 : IN$ = "*" : GOSUB 660 : IF A-0 THEN IF NOT EOF(FI) THEN 90 : ELSE PRINT " NOT ASCII FILE OR NO STARTING ADDRESS" : END
GO 100 A$ = STRING$ (20, 32) + "A " + r$ : NL$(x) = A$ - R$ : IN$ = " : " : GOSUB 660 : SG = VAL ("&H" + L$) : OF = VAL ("&H" + R$)
OI 110 WHILE NOT EOF (FI)
CM 120 LINE INPUT #FI, A$ : PRINT A$
OM 130 GOSUB 730 : IF A = 0 THEN 280
EM 140 IN$ = "INCLUDE" : GOSUB 660 : IF A>0 THEN FI = FI + 1 : OPEN R$ FOR INPUT AS #FI : GOTO 280
OH 150 IN$ = "EQU" : GOSUB 660 : IF A>0 THEN L = L + 1 : GOSUB 720 : AD$(L) = R$ : R$ = L$ : GOSUB 720 : L$ (L) = R$ : GOTO 280
ND 160 IN$ = "=" : GOSUB 660 : IF A>0 THEN L = L + 1 : GOSUB 720 : T$ = R$ : R$ = L$ : GOSUB 720 : L$ (L) = R$ : SG$ (L) = T$ : GOTO 280
FO 170 X = X + 1
DI 180 IN$ = "." : GOSUB 660 : IF A = 0 THEN 200 : ELSE A$ = R$ : R$ = L$ : GOSUB 720 : L = L + 1 : L$ (L) = R$ : SC$ (X) = SC$ (X) + R$ + "." : IF A$ = "" THEN IF NOT EOF (FI) THEN LINE INPUT #FI, A$ : PRINT A$ : GOSUB 730 : GOTO 180 : ELSE A$ = "DB" : GOSUB 200
GN 190 IN$ = "DB" : GOSUB 660 : T = A : IN$ = "DW" : GOSUB 660 : IF T + A<0 THEN SG$ (L) = "CS : "
FN 200 IN$ = "" : GOSUB 660 : IF A<0 THEN A$ = L$ + HEX$ (ASC (R$)) + RIGHT$ (R$, LEN (R$)-1)
GA 210 IN$ = "DUP" : GOSUB 660 : IF A = 0 THEN 250 : ELSE R = VAL ("&H" + R$) : T$ = L$ + " " : A$ = R$ : IN$ = "(" : GOSUB 660 : A$ = R$ : IN$ = ")" : GOSUB 660 : D$ = L$ : FOR N = 1 TO R
LO 220 IF LEN (T$ + D$)>73 THEN T$ = T$ + D$ : ELSE NL$ (X) = SC$ (X) = SC$ (X) + T$ : X = X + 1 : T$ = LEFT$ (T$, 2) + " " + D$
BK 230 IF N<R AND LEN (T$ + D$",")<74 THEN T$ = T$ + ","
LE 240 NEXT : IF LEN (T$ + R$) > = 73 THEN NL$ (X) = T$ : SC$ (X) = SC$ (X) + T$ : X = X + 1 : A$ = LEFT$ (T$, 2) + " " + RIGHT$ (R$, LEN (R$)-1) : GOTO 210 : ELSE A$ = T$ + R$ : GOTO 210
OH 250 SC$ (X) = SC$ (X) + A$
AD 260 IN$ = "OFFSET" : GOSUB 660 : IF A>0 THEN A$ = L$ + R$
HL 270 NL$ (X) = A$
BO 280 WEND : CLOSE FI : FI = FI - 1 : IF FI>0 THEN 110
EH 290 X = X + 1 : NL$ (X) = " " : X = X + 1 : NL$ (X) = "Q" : EX = 1
PN 300 WHILE EX = 1 : EX = 0 : FOR M = 2 TO L : IF LEN(L$ (M))>LEN(L$ (M - 1)) THEN SWAP L$ (M), L$ (M - 1) : SWAP AD$ (M), AD$ (M - 1) : SWAP SG$ (M), SG$ (M - 1) : EX = 1
KN 310 NEXT : WEND : L = L - 1
JB 320 FOR M = 1 TO L : A$ = SG$ (M) : IN$ = ":" : GOSUB 660 : IF A>0 THEN IF R$>"" THEN AD$ (M) = " [" + R$ + "]" : SG$ (M) = L$ + " : "
NF 330 NEXT
DP 340 CLS : OPEN "{MA}.1" FOR OUTPUT AS #2 : FOR N = 1 TO X : A$ = NL$ (N) : FOR M = 1 TO L
KB 350 IN$ = L$ (M) : GOSUB 660 : IF A = 0 THEN 410
BP 360 IF AD$ (M) >"" THEN : A$ = SG$ (M) + L$ + " " + AD$ (M) + R$ : NL$ (N) = A$ : GOTO 350
LK 370 IF LEFT$ (A$, 4) = "CALL" THEN 400
PA 380 IF LEFT$ (A$, 1) = "J" THEN A$ = "mov ax, bx" : GOTO 410 'PREVENTS OUT OF RANGE ERROR
AG 390 IN$ = "OFFSET" : A$ = SC$ (N) : T1$ = L$ : T2$ = R$ : GOSUB 660 : IF A = 0 THEN A$ = SG$ (M) + T1$ + "[00]" + T2$ : GOTO 410 : ELSE L$ = T1$ : R$ = T2$
HM 400 A$ = L$ + " 00" + R$
PE 410 NEXT M
KG 420 PRINT #2, A$ : PRINT A$ : NEXT : CLOSE
OL 430 EX = 1 : WHILE EX = 1 : EX = 0 : FOR N = 1 TO L : IF AD$ (N) >"" THEN FOR M = N TO L : L$ (M) = L$ (M + 1) : AD$ (M) = AD$ (M + 1) : SG$ (M) = SG$ (M + 1) : NEXT : L = L - 1 : EX = 1
GL 440 NEXT : WEND
MH 450 SHELL "DEBUG < {MA}.1 > {MA}.2"
DJ 460 AGAIN = 0 : OPEN "{MA}.2" FOR INPUT AS #1
BO 470 FOR N = 1 TO X : LINE INPUT #1, A$ : IF AGAIN = 0 THEN IF MID$ (A$, 6, 4)<>OF$ (N) THEN AGAIN = 1
ND 480 OF$ (N) = MID$ (A$, 6, 4)
FB 490 IN$ = "^" : GOSUB 660 : IF A>0 THEN CLS : PRINT "ERROR!" : CLEAR : CLOSE : SHELL "TYPE {MA}.2" : END
CD 500 FOR M = 1 TO L : T$ = A$ : A$ = SC$ (N) : IN$ = L$ (M) + "." : GOSUB 660 : IF A>0 THEN AD$ (M) = OF$ (N) : A$ = T$
GJ 510 NEXT : INPUT #1, JUNK$
CG 520 NEXT : CLOSE : CLS : OPEN"{MA}.1" FOR OUTPUT AS #2
ID 530 FOR N = 1 TO X : A$ = NL$ (N) : FOR M = 1 TO L
CC 540 IN$ = L$ (M) : GOSUB 660 : IF A = 0 THEN GOTO 580
PC 550 IF LEFT$ (A$, 1) = "J" OR LEFT$ (A$, 4) = "CALL" THEN GOTO 570
JK 560 IN$ = "OFFSET" : A$ = SC$ (N) : T1$ = L$ : T2$ = R$ : GOSUB 660 : IF A = 0 THEN A$ = SG$ (M) + T1$ + "[" + AD$ (M) + "]" + T2$ : GOTO 580 : ELSE L$ = T1$ : R$ = T2$
NE 570 A$ = L$ + " " + AD$ (M) + R$
IL 580 NEXT : PRINT #2, A$ : PRINT OF $ (N)" "; A$ : NEXT : CLOSE
JN 590 IF AGAIN = 1 THEN 450 'ONE MORE TIME
!
EA 600 CLS : PRINT "LABEL" TAB (30) "ADDRESS" : PRINT : FOR N = 1 TO L : PRINT L$ (N) TAB (30) SG$ (N) AD$ (N) : NEXT
PI 610 PRINT : INPUT "ENTER OUTPUR FILENAME OR HIT RETURN TO EXIT " A$
IN 620 IF A$=" "THEN 650 : ELSE IF A$ = F$ THEN PRINT : PRINT F$ "IS THE NAME OF YOUR SOURCE FILE." : PRINT : GOTL 610 : ELSE IN$ = "." : GOSUB 660
DJ 630 DEF SEG=SG : IF A>0 THEN IF R$ = "COM" OR R$ = "com" THEN OPEN A$ FOR OUTPUT AS #1 : FOR N=OF TO VAL ("&h" + OF $ (X-1)) : PRINT #1, CHR$ (PEE K (N)); : NEXT : CLOSE : GOTO 650
MF 640 BSAVE A$, OF, VAL ("&H" + OF$ (X-1) ) -OF
IB 650 INPUT "SCRATCH WORK FILES Y/N"; A$ : IF A$ = "Y" OR A$="Y" THEN CLEAR : SHELL "ERASE {MA}. ?" : END : ELSE END
LO 660 A=0 : B=0 : C=0 : F=0
MM 670 A=INSTR (F + 1, A$, IN$) : IF A= 0 THEN RETURN
AB 680 B=INSTR (B + 1, A$, CHR$ (34)) : IF B>0 THEN IF B<A THEN C = C + 1 : GOTO 680
MB 690 IF (C AND 1) THEN F=A : GOTO 670
EG 700 L$ = LEFT$ (A$, A-1) : R$ = RIGHT$ (A$, LEN (A$) - LEN (IN$) - A + 1 ) : GOSUB 710 : GOSUB 720 : RETURN
CL 710 IF RIGHT$ (L$, 1) = " " THEN L$ = LEFT$ (L$, LEN (L$)-1) : GOTO 710 : ELSE RETURN
PG 720 IF LEFT$ (R$, 1) =" " THEN R$=RIGHT$ (R$, LEN (R$)-1) : GOTO 720 : ELSE RETURN
HI 730 IN$ = STR$ (VAL(A$)) : IN$=RIGHT$ (IN$, LEN (IN$)-1) : GOSUB 660 : GOSUB 720 : A$ = R$
LN 740 IN$ = ";" : GOSUB 660 : IF A>0 THEN A$ = L$
HJ 750 IN$ = "%" : GOSUB 660 : A>0 THEN V= VAL (R$) : IN$ = "%" + RIGHT$ (STR$ (V), LEN (STR$ (V)) -1) : GOSUB 660 : A$=L$ + " " + HEX$ (V) + R$ : GOTO 750
IG 760 A=LEN (A$) : RETURN
EF 770 IF ERR = 53 THEN PRINT "File not found" : RESUME 50
OD 780 ON ERROR GOTO 0

Typing Note : Programs 2–4 are not BASIC programs. Read the typing instructions in the article before you enter these listings.

Program 2 : HEXCONV.ASM

BJ 100; HEXCONV.ASM - FROM COMPUTE!'s beginners guide to machine language
NP 110; MINI ASM VERSION
JG 120 * 1C00 : 100; OFFSET OF 100 H - COM FILE
NO 130;
EO 140 CR	EQU %13; ALL NUMBERS IN HEX UNLESS PRECEDED BY A PERCENT SIGN
NA 150 LF	EQU %10
NG 170;
DI 200 INCLUDE STACK.LIB; INCLUDES PROGRAM 3
OM 225;
HO 260	PUSH DS
FG 270	MOV AX, 0
KD 280	PUSH AX
IE 290	MOV CX, 0
MN 300	ANOTHER.
LL 310	MOV AX, CX
PM 320	CALL WORD OUT
FA 330	MOV DL, CR
CB 340	MOV AH, 2
NO 350	INT 21
AC 360	MOV DL, LF
CH 370	MOV AH, 2
NE 380	INT 21
FC 390	INC CX
LK 400	JNZ ANOTHER
CL 410	RETF; USE RETF FOR FAR RETURN
QO 420 ASCIINUMS. DB"0123456789 ABCDEF"; USE QUOTES RATHER THAN APOSTROPHE
MI 430 WORD OUT.
NJ 440	PUSH CX
LF 450	PUSH BX
OD 460	PUSH DX
IC 470	MOV CH, 4
MA 480 LOOP1.	MOV CL, 4
CH 490	ROL AX, CL
JG 500	PUSH AX
CB 510	AND AL, F
BP 520	MOV BX, OFFSET ASCIINUMS; "CS : " PREFIX AUTOMATICALLY PUT IN
FC 530	XLAT; ASCIINUMS LEAVE OFF ASCIINUMS - IT's implied
JO 540	MOV DL, AL
CF 550	MOV AH, 2
NR 560	INT 21
KG 570	POP AX
QL 580	DEC CH
MD 590	JNZ LOOP1
OI 600	POP DX
LA 610	POP DX
MH 620	POP CX
MK 630	RET; NEAR RETURN

program 3 : STACK.LIB

NM 10; STACK.LIB
HE 20	MOV SP, OFFSET TOP OF STACK-1
KH 30	MOV AX, CS
HB 40	MOV SS, AX
HG 50	JMP START OF PROGRAM
DJ 60	DB DUP %128 (0); 256 BYTES FOR STACK
PN 70	TOP OF STACK.
GI 80	START OF PROGRAM.

Program 4 : CLS.LIB

LA 100; * * * CLEAR SCREEN ROUTINE
GP 110 CLS.; CALL ROUTINE USING CLS AS DEFINED HERE.
FJ 120 MOV CX, 0
EB 130 MOV DL, %79
JE 140 MOV DH, %24
MF 150 MOV AL, 0
QM 160 MOV BH, 7
PJ 170 MOV AH, 6
NH 180 INT 10
DH 190 RET