Manual
Manual
CHAPTER 2
2. Protected mode prevents a bug in one program from affecting other programs
running at the same time. Almost any applications can benefit. For example, a
word processing program that crashes does not prevent a graphics drawing
program from continuing to run at the same time. Or, two programs monitoring
output information from separate machines in a manufacturing system can operate
independently of each other.
4. Expanded memory was designed to allow large data blocks such as spreadsheets
and documents to be kept in memory. This data could be kept in memory above
the 640K barrier imposed by DOS.
6. Conventional memory can only be accessed by the system bus, because it is
external to the CPU. The system bus is much slower than the CPU, forcing the
latter to wait for requested data.
8. BX is called a base register and BP is called a base pointer.
10. The CS register holds the base location of all executable instructions (code) in a
program. The DS register is the default base location for variables. The CPU
calculates their locations using the segment value in DS. The SS register contains
the base location of the stack. The ES register is an additional base location for
memory variables.
12. The CX register is used as a loop counter.
14. The CS (code segment) register.
16. The BP register.
18. Control flags: Interrupt flag, Trap flag, and Direction flag.
20. The Carry flag singals unsigned overflow.
22. The Parity flag. If there is an even number of bits, the Parity is even. If there is an
odd number of bits, the Parity is odd.
24. The interrupt vector table.
26. No, because the two adapters have different base addresses.
28. The stack segment is the base location of the stack, pointed to by the SS register.
30. Absolute.
32. The CS and IP registers.
34. Right after reading the config.sys file during the DOS boot sequence.
36. AX = 1234h, BX = 15FAh
CHAPTER 3
2. Assembly language mnemonic example: MOV
4. Yes, following the instruction.
CHAPTER 3 3
20. A directive is a statement that affects either the program listing or the way
machine code is generated. An instruction is only executed at runtime.
22. Identifier names may contain the following special characters: ?, _ (underscore),
@, $, . (period).
24. Examples of labels:
L1:
Start:
Begin_loop:
??0001:
26. (quote) Segments are the building blocks of programs: The code segment is where
program instructions are stored; the data segment contains all variables, and the
stack segment contains the program's runtime stack. The extra segment can be
used for any purpose, usually data.
28. Code example:
mov ax,@data
mov ds,ax
30. immediate.
32. The ENDP directive.
34. a. register, immediate
b. register, direct
c. register, immediate
d. register, indirect
36. Code example:
filename db "myfile.dta"
4 IRVINE: SOLUTIONS MANUAL
40. To clarify what the book said, IP can never be a destination operand. Segment
registers can be destination operands, only if the source operand is a general-
purpose register such as AX, BX, CX, DX, SI, BP, or DI.
42. Code example:
arrayptr dw intarray
44. AX = 0100h
CHAPTER 4
2. The assembler can create two files, proj1.lst and proj1.obj.
4. The R indicates that the operand is segment relocatable.
6. No, test.obj must be linked to produce the file test.exe.
8. Memory models: tiny, small, compact, medium, large, huge. (Optional: flat).
10. Each page of the listing file containes 55 vertical lines and 132 vertical columns.
12. A label outside the current module is external.
14. Referring to the example, if the code segment starts at 18400h, the data segment
starts at 18430 and the stack starts at 18440.
16. Type Length Size
var1 4 5 20
var2 2 10 20
var3 1 1 1
msg 1 1 1
18. PTR is required because the notation [si] does not indicate the memory operand's
size.
20. No, the program will halt before label L2 is reached.
22. Errors:
Line 5: Change .data to .code.
Line 6: Insert the following two lines:
mov ax,@data
mov ds,ax
CHAPTER 5 5
24. a. AX = 003Ah
b. BX = 0120h
c. BX = 003Ah
d. AX = 011Eh
e. ptr2 = 003Ah
CHAPTER 5
2. A software interrupt is activated when a program executes the INT instruction.
4. The numbers in the interrupt vector table are 32-bit pointers to interrupt service
routines that are either part of the operating system or have been installed by
software applications.
6. Because each computer has slightly different hardware, with devices made by
different manufacturers. An application program would be enormously complex if
it had to take into account all the possible hardware configurations of the
computers running the program.
8. DOS function calls are more hardware compatible than BIOS interrupts, and they
are part of the memory-resident part of DOS.
10. Redirection provides flexibility to users that want programs to read input/output
data from various sources.
12. NUL.
14. Code example:
.data
message db 'Hello, world!$' (1)
.code
mov ax,@data (2)
mov ds,ax
mov ah,9 ; string output (3)
mov dx,offset message (4)
int 21h
16. Use INT 16h when reading extended keys from the keyboard.
18. The chapter suggests using INT 21h function 6. (INT 16h can also be used.)
20. The carriage return (0Dh) character.
6 IRVINE: SOLUTIONS MANUAL
CHAPTER 6
2. Unsigned comparisons use the Carry and Zero flags.
4. The JCXZ instruction.
6. The JB instruction jumps base on unsigned comparison. The JL instruction is
based on signed comparisons.
8. The JB instruction will be executed if the Carry flag is set.
10. Both AL and BL equal 3Fh. (The usual prologue code of mov ax,@data \ mov
ds,ax is assumed to have been executed.)
12. AL = 01 and BL = 0CAh.
14. CX = 0268h, DX = 01FEh, SI = (offset val2), val2 = 3FD7h.
CHAPTER 7
2. The C and C++ languages contain bitwise shift operators, << and >>.
4. The ROL instruction copies the highest bit into the Carry flag and the lowest bit
position. RCL, which is similar, copies the Carry flag into the lowest bit position.
6. The SAR instruction shifts to the right and replicates the Sign bit.
8. Code example:
shr al,1
CHAPTER 7 7
jnc next
or al,10000000b
next:
28. Code example that divides 20000000h by 10h. We avoid a divide overflow
condition by performing 16-bit division on the upper word of the quotient first.
The remainder from this operation is added to the lower word of the quotient, and
16-bit division is performed again:
dividend dd 20000000h
result_lo dw 0
result_hi dw 0
.code
main proc
mov ax,@data ; init data segment
mov ds,ax
mov dx,0
mov ax,word ptr dividend+2 ; high word of dividend
mov bx,10h ; divisior
div bx ; ax = quotient, dx = remainder
mov result_hi,ax ; quotient, high word
mov ax,word ptr dividend ; low word of dividend
div bx ; ax = quotient, dx = remainder
mov result_lo,ax ; quotient, low word
CHAPTER 8
2. Code example:
mPushData macro
push ax
push bx
push cx
push dx
endm
mPopData macro
pop dx
pop cx
pop bx
pop ax
endm
4. Code example:
CHAPTER 8 9
L1:
call Readint ; input integer into AX
call Crlf
mov [si],ax
add si,2
loop L1
endm
6. Code example:
mTestJump macro dest,source,result,label
test dest,source
j&result label
endm
mLocate 10,20
0005 BB 0000 1 mov bx,0
0008 B4 02 1 mov ah,2
000A B6 14 1 mov dh,20
000C B2 0A 1 mov dl,10
000E CD 10 1 int 10h
mLocate row,col
0010 BB 0000 1 mov bx,0
0013 B4 02 1 mov ah,2
0015 B6 16 1 mov dh,col
0017 B2 12 1 mov dl,row
0019 CD 10 1 int 10h
CHAPTER 9
2. The hexadecimal contents of ASCII 4096 would be 31h,30h,39h, and 36h.
4. No, XLAT can only work with tables of 8-bit values.
6. XLAT does not affect the flags.
10 IRVINE: SOLUTIONS MANUAL
8. AL = 37h, the value at offset 6 in the table. (The offset of ptr1, by the way, is
irrelevant.)
10. To convert 302h (770d) from binary to ASCII decimal, we repeatedly divide the
number by 10 and take each remainder as one of the resulting ASCII digits:
770 / 10 = 77 remainder 0 (convert to "0")
77 / 10 =7 remainder 7 (convert to "7")
7 / 10 =0 remainder 7 (convert to "7")
12. Converting ASCII "3F62" to binary requires multiplying each digit by 16:
(Sum * 16) + digit = Sum
(0* 16) + 3 = 3
(3* 16) + F = 63
(63 * 16) + 6 = 1014
(1014 * 16) + 2 = 16226
14. DS = 1234h and SI = ABCDh.
16. Code Example:
.data
inputlist db 5,26,45,96,88,128
COUNT = ($ - inputList)
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
getchar:
mov al,[si]
mov dl,al ; save the character
xlat validchars ; look up in table
cmp al,0FFh ; valid character?
jne next ; no: get next character
mov ah,2 ; display char in DL
int 21h
next:
inc si
loop getchar
CHAPTER 10 11
18. If AX equals 600h (1536d) before the loop starts, the value in DX each time the
loop repeats is the remainder after dividing by 10: { 6, 3, 5, 1 }
CHAPTER 10
2. The longer string is truncated to the available space in the shorter string's storage
area.
4. REP should be used. It automatically decrements CX.
6. DI will equal 000Bh.
8. Code example:
mov ax,@data ; init data segment
mov ds,ax
mov es,ax
lookforit:
std ; direction = down
mov al,'@' ; AL = byte to be found
mov di,offset bigstring ; get string offset
add di,biglen
dec di ; point to last byte
mov cx,biglen
repnz scasb ; repeat while NZ
inc di ; adjust DI when found
CHAPTER 11
2. Explanations of error codes:
ErrorNum/Function Explanation
0Fh -> 0Eh Trying to set default drive to nonexistent drive.
15h -> 36h Diskette was not in drive.
0Fh -> 47h Trying to get current directory of nonexistent drive.
03h -> 3Bh Trying to set current directory to nonexistent path.
05h -> 39h Trying to create subdirectory using the same name as an
existing directory or file.
10h -> 3Ah Trying to remove the current subdirectory.
12. A value of 2Eh indicates that the filename starts with a period. The parent
directory, for example, is indicated by a filename of "..". In DOS you move to the
parent directory by typing the following command:
CD ..
18. A value of 18h indicates a volume or directory name, which the program does not
display.
20. Code example: AX contains the previous cluster number, and DX contains the
offset into the FAT. Calculate the new cluster number and place it in AX. We
assume that DX contains the offset of the new cluster number, which might have
been calculated as follows:
mov dx,ax ; copy the number
shr dx,1 ; divide by 2
add dx,ax ; new cluster offset
Now we can use the offset in DX to obtain the new cluster number:
mov bx,dx ; use a base register
mov dx,fattable[bx] ; DX = new cluster value
shr ax,1 ; old cluster even?
jc E1 ; no: keep high 12 bits
and dx,0FFFh ; yes: keep low 12 bits
jmp E2
E1: shr dx,4 ; shift 4 bits to the right
E2: mov ax,dx ; return new cluster number
CHAPTER 12
2. Yes, function 3Ch creates the file for both input and ouput. The read-only
attribute only applies after the file is closed.
4. Error explanations:
· Rename file, Error 3: The new filename path contains directories that do not
exist.
· Delete file, Error 5: Access denied, meaning that the file is currently in use or its
read-only attribute bit is set.
· Set date/time, Error 6: An invalid file handle indicates that the file has not been
opened.
CHAPTER 12 13
· Remove directory, Error 10h: You cannot remove the current directory.
· Rename file, Error 11h: Not same device. A file cannot be rename across disk
boundaries.
· Find first matching file, Error 12h: No more file handles can be opened.
6. No, when you delete a file, all you need is its file handle.
8. An invalid file handle was used when trying to read the file. The file handle must
match that of a file that is currently open for input or input/output.
10. The buffer will contain "1234567890". Note: 0Dh and 0Ah are only inserted in
the buffer if the user types fewer than 10 characters before pressing the Enter key.
12. Yes, the file pointer is updated automatically.
14. Yes, the file pointer can be moved using Function 42h.
16. The offset would be 50 x 19, which equals 950.
18. 11000101: store = 6 department = 5
00101001: store = 1 department = 9
01010101: store = 2 department = 21
CHAPTER 13
2. An underscore character is prepended to all external names.
4. Near.
6. Yes, certain registers must be preserved. The requirements will vary among
different compilers written for the same language.
8. The calling program adds a value to the stack pointer that restores it to its state
before the subroutine arguments were pushed on the stack.
10. The C linker is case-sensitive, so any names exported by the assembly language
module must be recognized by the linker.
12. Near calls are required by the tiny, small, and compact memory models.
14. The flat memory model.
16. Stack frame, 1 parameter passed by far reference, large model. Let us assume that
the current value of SP is F002. The parameter segment and offset were pushed
first, then the segment:offset return address, then BP:
SS:F00A SS:F008 SS:F006 SS:F004 SS:F002
(paramSeg) (paramOfs) (returnSeg) (returnOfs) BP
18. The __fastcall calling method uses registers for all parameters, rather than
pushing parameters on the stack.
20. Inline assembly code is assembly language source code inserted into the body of
any C++ procedure. A C++ inline function, on the other hand, is an entire function
14 IRVINE: SOLUTIONS MANUAL
written in C++ whose machine code is inserted into a C++ program at the point at
which procedure is called. The latter approach eliminates the overhead of calling
and returning from the function.
22. Inline code is not portable to different computer systems, so its enclosing C++
program is also not portable.
24. Yes.
26. Yes.
28. No.
30. Yes.
32. Yes.
34. It returns a count of the number of elements in the array.
36. If the C++ compiler is allowed to mangle the names used in subroutine calls, the
linker cannot match these names to the names of external subroutines.
38. int = 2, enum = 2, float = 4, double = 8.
40. Code example:
mov eax,[bp+6]
CHAPTER 14
2. A pointer variable might contain the address of an array, and be used to iterate
over the array members. A pointer might contain the address of a procedure, and a
program could call the procedure via the pointer. Pointers are also used when
passing arguments to procedures by reference.
4. Both instructions move the same value to SI. The first, using OFFSET, moves an
immediate value that must be known at assembly time. The second instruction,
using LEA, is able to determine the location of a variable at runtime, and is
therefore more flexible.
6. The CLI instruction, which clears the Interrupt flag, disables hardware interrupts
until the flag is later set. This is done by system procedures that handle requests
from hardware devices. It is important that no other devices interrupt these
procedures until they have finished. (This information was taken from Chapter 15,
p. 545.)
8. HLT suspends a program until a hardware interrupt occurs. WAIT suspends the
main CPU until a coprocessor signals that it has completed an operation.
10. Errors:
a. Immediate port address must be between 0 and 255.
b. Second operand must be AL or AX.
CHAPTER 14 15
dseg segment
value1 dw 1000h,2000h
dseg ends ; error 3
28. Transient programs are loaded into memory long enough to be executed, and then
the memory they occupied is released when they finish.
30. A copy of the DOS command tail is stored at offset 80h in the program segment
prefix area (at the beginning of the program).
32. Load module.
CHAPTER 15
2. The 8253 Programmable Interval Timer/Counter.
4. The control unit (also called the bus interface unit).
6. The arithmetic logic unit (ALU).
8. The control unit.
10. Code examples:
dd +1.5E+02 ; decimal short real
dd 3F800000r ; encoded short real
dq +1.5E+10 ; decimal long real
dq 3F00000000000000r ; encoded long real
dt 123456789012345678 ; 10-byte real in BCD format
dq 123456789d ; 8-byte decimal integer
12. Interrupt 9.
14. File I/O has a lower priority than keyboard I/O, so the key will be placed in the
buffer first.
16. It is store at segment 0, offset 10h x 4, which is 0000:0040h.
18. Math coprocessor instructions cannot reference the main CPU registers.
17
-a 100
1D99:0100 mov ax,1
1D99:0103 mov bx,2
1D99:0106 mov cx,3
1D99:0109 mov dx,4
1D99:010C mov si,5
1D99:010F mov di,6
1D99:0112 mov bp,7
1D99:0115 mov sp,8
-d FFFF:0005
FFFF:0000 30 31 2F-33 30 2F 39 36 00 FC 00 01/30/96...
-r ds
DS 1D99
:F000
-d 0 FF
F000:0000 41 4D 49 42 49 4F 53 28-43 29 41 4D 49 30 32 2F AMIBIOS(C)AMI02/
F000:0010 30 32 2F 31 39 39 35 20-44 61 74 65 3A 2D 30 32 02/1995 Date:-02
F000:0020 2F 30 32 2F 39 35 20 28-43 29 31 39 38 35 2D 31 /02/95 (C)1985-1
F000:0030 39 39 32 2C 41 4D 49 41-6D 65 72 69 63 61 6E 20 992,AMIAmerican
F000:0040 4D 65 67 61 74 72 65 6E-64 73 20 49 6E 63 2E 2C Megatrends Inc.,
F000:0050 28 43 29 31 39 39 33 2D-31 39 39 35 2C 20 49 6E (C)1993-1995, In
F000:0060 74 65 6C 20 43 6F 72 70-6F 72 61 74 69 6F 6E 43 tel CorporationC
F000:0070 6F 70 79 72 69 67 68 74-20 49 6E 74 65 6C 20 43 opyright Intel C
F000:0080 6F 72 70 6F 72 61 74 69-6F 6E 41 6C 6C 20 52 69 orporationAll Ri
F000:0090 67 68 74 73 20 52 65 73-65 72 76 65 64 2E 41 6C ghts Reserved.Al
F000:00A0 6C 20 50 72 6F 64 75 63-74 20 6E 61 6D 65 73 20 l Product names
F000:00B0 61 72 65 20 74 72 61 64-65 6D 61 72 6B 73 20 6F are trademarks o
F000:00C0 66 20 74 68 65 69 72 20-72 65 73 70 65 63 74 69 f their respecti
F000:00D0 76 65 20 43 6F 6D 70 61-6E 69 65 73 2E 00 00 00 ve Companies....
F000:00E0 66 60 2E A0 F3 04 E8 32-03 0F B6 C0 50 B4 00 B0 f`.....2....P...
F000:00F0 01 BB 0B 00 B9 00 00 66-33 FF 66 33 F6 E9 BC E9 .......f3.f3....
-E 200 36
CHAPTER 2 19
-A 100
1D99:0100 MOV DL,[200]
1D99:0104 MOV [201],DL
-T
-T
-D 200,201
1D99:0200 36 36 (after executing the program)
-A 100
1D99:0100 MOV AL,1
1D99:0102 SUB AL,2
1D99:0104
-T
-A 100
1D99:0100 mov ax,2000
1D99:0103 mov si,ax
1D99:0105 mov bx,si
1D99:0107 mov ds,bx
1D99:0109 int 20
1D99:010B
20 IRVINE: SOLUTIONS MANUAL
T
T
T
T
T
R IP
100
-a 100
1D99:0100 mov al,FF
1D99:0102 inc al
1D99:0104 sub al,2
1D99:0106 mov dl,al
1D99:0108 add dx,2
1D99:010B int 20
1D99:010D
-t
>> Comments:
>> ZR = 1 because AL was rolled over to zero. CF = 0 because the INC
>> instruction does not set the Carry flag.
>> Comments:
>> CF = 1 because subtracting 2 from 0 is an invalid unsigned operation.
>> OF = 0 because AL contains a valid signed result. SF = 1 because
>> the value in AL is negative.
-t
>> Comments:
>> SF = 0 because DL is positive, CF = 0 because DX did not create an
>> unsigned overflow.
-d b800:0
B800:0000 42 07 38 07 30 07 30 07-3A 07 30 07 31 07 46 07 B.8.0.0.:.0.1.F.
B800:0010 30 07 20 07 20 07 32 07-30 07 20 07 30 07 37 07 0. . .2.0. .0.7.
B800:0020 20 07 32 07 30 07 20 07-30 07 37 07 20 07 33 07 .2.0. .0.7. .3.
B800:0030 44 07 20 07 30 07 37 07-20 07 33 07 32 07 20 07 D. .0.7. .3.2. .
B800:0040 30 07 37 07 2D 07 33 07-30 07 20 07 30 07 37 07 0.7.-.3.0. .0.7.
B800:0050 20 07 33 07 33 07 20 07-30 07 37 07 20 07 33 07 .3.3. .0.7. .3.
B800:0060 45 07 20 07 30 07 37 07-20 07 32 07 30 07 20 07 E. .0.7. .2.0. .
B800:0070 30 07 37 07 20 07 20 07-20 07 2E 07 2E 07 20 07 0.7. . . ..... .
-d
B800:0080 2E 07 30 07 2E 07 30 07-2E 07 3A 07 2E 07 30 07 ..0...0...:...0.
B800:0090 2E 07 30 07 2E 07 37 07-2E 07 20 07 20 07 20 07 ..0...7... . . .
B800:00A0 42 07 38 07 30 07 30 07-3A 07 30 07 32 07 42 07 B.8.0.0.:.0.2.B.
B800:00B0 30 07 20 07 20 07 33 07-30 07 20 07 30 07 37 07 0. . .3.0. .0.7.
B800:00C0 20 07 33 07 33 07 20 07-30 07 37 07 20 07 33 07 .3.3. .0.7. .3.
B800:00D0 30 07 20 07 30 07 37 07-20 07 33 07 37 07 20 07 0. .0.7. .3.7. .
B800:00E0 30 07 37 07 2D 07 32 07-30 07 20 07 30 07 37 07 0.7.-.2.0. .0.7.
B800:00F0 20 07 32 07 45 07 20 07-30 07 37 07 20 07 32 07 .2.E. .0.7. .2.
-a 100
22 IRVINE: SOLUTIONS MANUAL
The registers were popped off the stack in the same order as the PUSH
instructions, resulting in their values being reversed.
-e 100 10,20,30
-a 100
1D99:0100 mov dl,[100]
1D99:0104 add dl,[101]
1D99:0108 add dl,[102]
1D99:010C
-a 200
1D99:0200 dw 0102,0304,0506,0
-a 100
1D99:0100 mov ax,[200]
1D99:0103 add ax,[202]
1D99:0107 add ax,[204]
1D99:010B mov [206],ax
-d 206,207
1D99:0200 0C 09
-a 100
1D99:0100 mov ax,20
24 IRVINE: SOLUTIONS MANUAL
-d 100,109
1D99:0100 B8 20 00 BB 10 00 01 D8-CD 20
-a 200
1D99:0200 db B8,20,00,BB,10,00,01,D8,CD,20
1D99:020A
-g = 200
Explanation:
CHAPTER 3
.model small
.stack 100h
.data
.code
main proc
mov ax,@data ; init data segment
mov ds,ax
mov ax,1234h
mov bx,ax
CHAPTER 3 25
mov cx,ax
add ch,al ; CF = 0, SF = 0, ZF = 0, OF = 0
add bl,ah ; CF = 0, SF = 0, ZF = 0, OF = 0
add ax,0FFFFh ; CF = 1, SF = 0, ZF = 0, OF = 0
dec bx ; CF = 1, SF = 0, ZF = 0, OF = 0
inc ax ; CF = 1, SF = 0, ZF = 0, OF = 0
.model small
.stack 100h
.data
array db 31h,32h,33h,34h
.code
main proc
mov ax,@data ; init data segment
mov ds,ax
mov ah,2
mov dl,array
int 21h
mov ah,2
mov dl,array+1
int 21h
mov ah,2
mov dl,array+2
int 21h
mov ah,2
mov dl,array+3
int 21h
.model small
26 IRVINE: SOLUTIONS MANUAL
.stack 100h
.data
ThreeBytes db 10h,20h,30h
TheSum db ?
.code
main proc
mov ax,@data ; init data segment
mov ds,ax
mov al,ThreeBytes
add al,ThreeBytes+1
add al,ThreeBytes+2
mov TheSum,al
.model small
.stack 100h
.data
aString db "this is a string containing lowercase letters"
strSize = ($ - aString)
.code
main proc
mov ax,@data ; init data segment
mov ds,ax
.model small
.stack 100h
.386
.data
longVals dd 12345h,87654h
.code
main proc
mov ax,@data ; init data segment
mov ds,ax
mov eax,12345678h
mov ebx,eax
mov ecx,longVals
mov edx,longVals+4
.model small
.stack 100h
.386
.data
array dw 1,11 dup(0)
.code
main proc
mov ax,@data ; init data segment
mov ds,ax
mov di,offset array
mov cx,12
L1:
mov ax,[di] ; get current value
add ax,ax ; double it
inc di
mov [di],ax ; save it for the next loop iteration
Loop L1
.model small
.stack 100h
.386
LOOP_COUNT = 22
.data
array dw 1,1,LOOP_COUNT dup(0)
.code
extrn Writeint:proc, Crlf:proc
main proc
mov ax,@data ; init data segment
mov ds,ax
mov di,offset array
mov bx,10
mov cx,LOOP_COUNT
L1:
mov ax,[di-4] ; sum the previous two values
add ax,[di-2]
mov [di],ax ; save current value
call Writeint ; display it
call Crlf
add di,2
Loop L1
CHAPTER 4
From Chapter 4 onward, the book's Link Library (irvine.lib) is used by our solution pro-
grams. The following include file provides the necessary external declarations for the
programs in this chapter:
CHAPTER 4 29
.model small
.stack 100h
.data
prompt1 db "Enter the first integer: ",0
prompt2 db "Enter the second integer: ",0
result db "The sum of the integers is: ",0
num1 dw ?
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
mov dx,0500h
call Gotoxy
add ax,num1
30 IRVINE: SOLUTIONS MANUAL
.model small
.stack 100h
.data
byteArray db 10,20,30
wordArray dw 1000,2000,3000
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
mov ax,0
mov bx,offset byteArray
mov al,[bx]
mov bx,10
call Writeint
call Crlf
mov ax,0
mov bx,offset byteArray+1
mov al,[bx]
mov bx,10
call Writeint
call Crlf
mov ax,0
mov bx,offset byteArray+2
mov al,[bx]
mov bx,10
call Writeint
call Crlf
.model small
.stack 100h
.data
wordArray dw 1000,2000,3000
sum dw ?
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
mov sum,0
mov cx,3 ; loop counter
mov bx,offset wordArray
mov ax,sum
mov bx,10 ; display the sum
call Writeint
call Crlf
int 21h
main endp
end main
.model small
.stack 100h
.data
aList db 6Fh,0B4h,1Fh
sum dw 0 ; changed to a 16-bit variable
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
.model small
.stack 100h
.data
wlist dw 1000h,2000h,7000h,03FFh
LOOP_COUNT = ($ - wlist) / (type wlist)
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
mov cx,LOOP_COUNT
mov si,offset wlist
mov ax,0
L1:
add ax,[si] ; watch the Carry flag for
add si,type wlist ; unsigned overflow!
loop L1
.model small
.stack 100h
.386
LOOP_COUNT = 20
.data
array dd LOOP_COUNT dup(0)
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
mov cx,LOOP_COUNT
mov si,offset array
L1:
mov eax,101 ; specify range: 0 - 100
call Random_range ; generate random integer
mov [si],eax ; save the number
34 IRVINE: SOLUTIONS MANUAL
.model small
.stack 100h
.386
STRING_SIZE = 50
.data
randString db STRING_SIZE dup (0),0
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
mov cx,STRING_SIZE
mov si,offset randString
L1:
mov eax,26 ; specify range: 0 - 25
call Random_range ; generate random integer
add al,'A' ; offset to start of alphabet
mov [si],al ; save in string
inc si
loop L1
.model small
.stack 100h
.386
COUNT = 500
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
mov cx,COUNT
L1:
mov eax,25 ; specify row number
call Random_range ; generate random integer
mov dh,al
mov eax,80 ; specify column number
call Random_range ; generate random integer
mov dl,al
call Gotoxy ; locate cursor
mov ah,2 ; display the character
mov dl,'X'
int 21h
Loop L1
.model small
.stack 100h
.386
one = 1
two = 2
three = one * two + 10 / 4
four = 418 mod 6
five = four - one
string equ <"This, naturally, is a string",0>
.data
msg db string
.code
include library.inc
36 IRVINE: SOLUTIONS MANUAL
main proc
mov ax,@data ; init data segment
mov ds,ax
mov al,one
mov bl,two
mov cl,three
mov dl,four
mov ax,one
mov bx,two
mov cx,three
mov dx,four
mov si,five
.model small
.stack 100h
.386
.data
string db "Source string",0 ; string to be copied
STRSIZE = ($ - string)
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call Clrscr
mov si,offset string
mov di,offset dest
mov cx,STRSIZE
L1:
mov al,[si] ; get character from the source
mov [di],al ; copy it to the destination
inc si ; increment both pointers
inc di
CHAPTER 4 37
.model small
.stack 100h
.386
.data
string db "Source string",0 ; string to be copied
STRSIZE = ($ - string) - 1
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call Clrscr
mov si,offset string
add si,(STRSIZE - 1)
mov di,offset dest
mov cx,STRSIZE
L1:
mov al,[si] ; get character from the source
mov [di],al ; copy it to the destination
dec si ; move both pointers
inc di
loop L1 ; repeat loop
.model small
.stack 100h
.386
COUNT = 5
.data
array1 dw 1000h,2000h,3000h,4000h,5000h
arraySize = ($ - array1) / (type array1)
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call Clrscr
mov si,0
mov cx,COUNT
L1:
mov ax,array1[si] ; get integer from array1
mov array2[si],ax ; copy it to array2
add si,type array1 ; index to next position
loop L1 ; repeat loop
.model small
.stack 100h
.386
COUNT = 5
INSERT_POSITION = 2 ; first element is (0)
INSERT_VALUE = 2500h
.data
array1 dw 1000h,2000h,3000h,4000h,5000h,0
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
L1:
mov ax,array1[si-2] ; get integer from the array
mov array1[si],ax ; move it back one position
sub si,type array1 ; point to previous
loop L1 ; repeat loop
call Clrscr
mov cx,COUNT+1
mov si,0
L2:
mov ax,array1[si] ; get integer from the array
mov bx,16 ; display it in hexadecimal
call Writeint
call Crlf
add si,type array1 ; point to next
loop L2 ; repeat loop
.model small
.stack 100h
.386
.data
array dd 1,2,3,4,5,6,7,8,9,10
COUNT = ($ - array) / (type array)
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call Clrscr
mov cx,COUNT
mov si,0
L1:
mov eax,array[si] ; get integer from the source
mov dest[si],eax ; copy it to the destination
mov bx,10 ; display it in decimal
call Writelong
call Crlf
add si,type array ; increment index
loop L1 ; repeat loop
.model small
.stack 100h
.386
COUNT = 50
.data
CHAPTER 4 41
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
mov cx,COUNT
mov si,0
mov ax,1
L2:
mov eax,COUNT ; rand value, 0..(COUNT - 1)
call Random_range
mov di,ax
shl di,1 ; multiply by 2 for 16-bit values
mov ax,array[si] ; exchange [si] with [di]
xchg ax,array[di]
mov array[si],ax
add si,type array ; increment index
loop L2 ; repeat loop
call Clrscr
mov cx,COUNT
mov si,0
mov bx,10 ; decimal radix
call Crlf
mov ax,4c00h ; end program
42 IRVINE: SOLUTIONS MANUAL
int 21h
main endp
end main
.model small
.stack 100h
.386
.data
array dd 1,2,3,4,5,6,7,8,9,10
COUNT = ($ - array) / (type array)
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call Clrscr
mov cx,COUNT
mov si,offset array
mov di,dest_last
L1:
mov eax,[si] ; get integer from the source
mov [di],eax ; copy it to the destination
add si,type array ; increment index
sub di,type array
loop L1 ; repeat loop
mov cx,COUNT
mov si,offset dest
L2:
mov eax,[si]
mov bx,10 ; display it in decimal
call Writelong
call Crlf
add si,type dest ; increment index
loop L2
end main
.model small
.stack 100h
.386
.data
array dw 1000h,2000h,3000h,4000h,5000h
COUNT = ($ - array) / (type array)
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call Clrscr
mov cx,COUNT-1
mov si,0
L2:
mov ax,array[si] ; get integer from the array
mov bx,16 ; display it in hexadecimal
call Writeint
call Crlf
add si,type array ; point to next
loop L2 ; repeat loop
DeleteMember proc
DM2:
ret
DeleteMember endp
end main
.model small
.stack 100h
.data
myArray dw 1000h,2000h,3000h,4000h,5000h,6000h
COUNT = ($ - myArray) / (type myArray)
aPointer dw myArray
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
mov si,aPointer
mov cx,COUNT
CHAPTER 5
.model small
.stack 100h
.data
WAIT_FOR_KEY = 10h
ESC_KEY = 1Bh
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
L1:
mov ah,WAIT_FOR_KEY
int 16h ; get scan code in AH
cmp al,ESC_KEY ; check for Esc key
je L2 ; NEEDS EXPLANATION
L2:
mov ax,4c00h ; end program
int 21h
main endp
end main
; (Advanced assignment)
; Display the status of the Shift, CapsLock, and Alt keys
; in the lower right corner of the screen. Pass the color
; attribute as an argument.
.model small
.stack 100h
SHIFTKEY_MASK = 11b
CAPSLOCK_MASK = 1000000b
ALTKEY_MASK = 1000b
CHECK_KEYBOARD = 11h
GET_KYBD_FLAGS = 12h
STATUS_ROW = 24
STATUS_COL = 60
STATUS_LEN = 20
ESC_KEY = 1Bh
.data
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
mov bl,0Fh ; color attribute
Exit:
CHAPTER 5 47
ShowStatusKeys proc
mov dh,STATUS_ROW
mov dl,STATUS_COL
mov ah,bl
mov ah,GET_KYBD_FLAGS
int 16h ; keyboard status in AL
mov ah,bl ; AH = video attribute
L4:
ret
ShowStatusKeys endp
end main
.model small
.stack 100h
.data
aList dw 100h,200h,300h,400h,500h
COUNT = ($ - aList) / (type aList)
48 IRVINE: SOLUTIONS MANUAL
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
mov cx,COUNT
mov si,offset aList
mov cx,COUNT
.model small
.stack 100h
.data
string db " This text is in the window.$"
.Code
main proc
mov ax, @data ; init data segment
mov ds, ax
SetWindow:
mov ah, 06h ; function 6, initialize a window
mov al, 0Fh ; set lines to scroll at 15
mov bh, 70h ; attribute: black on white
mov ch, 05h ; start at row 5
mov cl, 0Ah ; column 10
mov dh, 14h ; end at row 20
CHAPTER 5 49
SetCursor:
mov ah, 02h ; call function 2, set cursor position
mov bh, 00h ; on video page 0 (current page)
mov dh, 0Ah ; at row 10
mov dl, 14h ; column 20
int 10h
DisplayText:
mov ah, 09h ; function 9, display string
mov dx, offset string ; offset of the string
int 21h
Exit:
mov ax, 4C00h ; exit program
int 21h
main endp
end main
.model small
.stack 100h
.data
string db " This text is in the window.$"
.code
main proc
mov ax,@data ; init data segment
mov ds,ax
SetWindow:
mov ah,6 ; scroll window up
mov al,0Fh ; set lines to scroll at 15
mov bh,70h ; inverse video attribute
mov ch,5 ; start at column 10
mov cl,0Ah ; row 5
mov dh,14h ; and end at column 70,
mov dl,46h ; row 20
int 10h
SetCursor:
mov ah,2 ; set cursor position
mov bh,0 ; on video page 0
mov dh,0Ah ; at row 10
mov dl,14h ; column 20
int 10h
DisplayText:
mov ah,9 ; display a string
mov dx,offset string ; point to the string
int 21h
50 IRVINE: SOLUTIONS MANUAL
WaitForKey:
mov ah,8 ; console input with
int 21h ; no echo
ScrollWindow:
mov ah,6 ; scroll window up
mov al,0 ; scroll all lines
mov bh,7 ; normal attribute
mov ch,7 ; start at row 7
mov cl,0Fh ; column 15
mov dh,12h ; and end at row 18
mov dl,44h ; column 68
int 10h
SetCursor2:
mov ah,2 ; set cursor position
mov bh,0 ; on video page 0
mov dh,0Ch ; at row 12,
mov dl,26h ; column 43
int 10h
WriteCharacter:
mov ah,9 ; write character and
mov al,'A' ; attribute at cursor position
mov bh,0 ; on video page 0
mov bl,87h ; normal video, blinking
mov cx,1 ; write it one time
int 10h
WaitForKey2:
mov ah,8 ; console input with
int 21h ; no echo.
ClearScreen:
mov ah,6 ; scroll a window
mov al,0 ; scroll all lines
mov bh,7 ; normal video attribute
mov ch,0 ; start at row 1
mov cl,0 ; column 1
mov dh,18h ; and end at row 25
mov dl,50h ; column 80
int 10h
Exit:
mov ax,4C00h ; end program
int 21h
main endp
end main
.model small
.stack 100h
.code
main proc
mov ax,@data ; init data segment
mov ds,ax
mov cx,10 ; loop counter
InputCharacter:
mov ah,1 ; character input with echo
int 21h
OutputCharacter:
mov ah,2 ; character output
mov dl,al ; character in dl
int 21h
loop InputCharacter ; repeat 10 times
.model small
.stack 100h
.code
main proc
mov ax,@data
mov ds,ax
mov ah,5 ; DOS: printer output
mov dl,15 ; code for Epson compressed type
int 21h
.model small
.stack 100h
.data
52 IRVINE: SOLUTIONS MANUAL
.code
main proc
mov ax,@data
mov ds,ax
mov ah,9
mov dx,offset aString
int 21h
mov ax,4C00h
int 21h
main endp
end main
.model small
.stack 100h
.data
stringSize db 80 ; size of input area
keysTyped db ? ; number of chars input
inputString db 80 dup("$") ; input chars stored here
crlf db 0Dh,0Ah,"$"
.code
main proc
mov ax,@data
mov ds,ax
mov ah,0Ah ; DOS function: input string
mov dx,offset stringSize
int 21h
mov ax,0
mov al,keysTyped
mov si,ax
mov inputString[si+1],0Ah ; linefeed character
.model small
.stack 100h
.data
string db "Enter lowercase letters: $"
.code
main proc
mov ax,@data ; init data segment
mov ds,ax
clear_window:
mov ah,7 ; scroll window down
mov al,0 ; clear entire window
mov cx,0 ; row 0, column 0
mov dx,184fh ; to row 24, column 79
mov bh,70h ; reverse video
int 10h
set_cursor:
mov ah,2 ; set cursor position
mov dx,0913h ; row 9, column 19
mov bh,0 ; video page 0
int 10h
display_prompt:
mov ah,9 ; display a string
mov dx,offset string
int 21h
input_character:
mov ah,8 ; input char, no echo
int 21h ; char is in AL
convert_character:
sub al,32d ; convert AL to uppercase
display_character:
mov ah,2 ; display character
mov dl,al ; character is in DL
54 IRVINE: SOLUTIONS MANUAL
int 21h
wait_for_key:
mov ah,8 ; get character, no echo
int 21h
jmp input_character ; get another character
Exit:
mov ax,4c00h ; end program
int 21h
main endp
end main
.model small
.stack 100h
LOOP_COUNT = 15
.data
char db 'A'
row db 5
col db 5
color db 1
.code
main proc
mov ax,@data ; init data segment
mov ds,ax
mov cx,LOOP_COUNT
L1:
push cx ; save loop counter
int 10h
inc col
inc char
inc color
.model small
.stack 100h
ulrow = 5
ulcol = 10
lrrow = 20
lrcol = 70
bwidth = (lrcol - ulcol) - 2
bheight =(lrrow - ulrow) - 2
ulcorner = 0DAh
urcorner = 0BFh
llcorner = 0C0h
lrcorner = 0D9h
hbar = 0C4h
vbar = 0B3h
crlf EQU <0Dh,0Ah>
.data
top db ulcorner
db bwidth dup (hbar) ; horizontal line
db urcorner,crlf,'$'
bottom db llcorner
db bwidth dup (hbar) ; horizontal line
db lrcorner,crlf,'$'
.code
main proc
56 IRVINE: SOLUTIONS MANUAL
mov ax,@data
mov ds,ax
call DrawBox
DrawBox proc
; Draw the top of the box.
call locate
mov ah,9 ; display string
mov dx,offset bottom
int 21h
ret
DrawBox endp
locate proc
push ax
push bx
push dx
mov ah,2
mov bh,0
mov dh,row
mov dl,col
int 10h
pop dx
pop bx
pop ax
CHAPTER 5 57
ret
locate endp
end main
.model small
.stack 100h
.286
TABLE_ENTRY_SIZE = 4
.data
; Format for table entry: top row, left column,
; bottom row, right column.
boxWidth dw ?
boxHeight dw ?
attribute db 7 ; white on black
58 IRVINE: SOLUTIONS MANUAL
row db ?
col db ?
.code
include library.inc
main proc
mov ax,@data
mov ds,ax
mov si,offset boxes ; point to box table
call Clrscr
L1:
cmp byte ptr [si],0FFh ; end of table?
je quit ; yes: quit
call draw_box ; SI points to box information
add si,TABLE_ENTRY_SIZE ; get next entry
jmp L1
quit:
mov ax,4C00h ; return to DOS
int 21h
main endp
draw_box proc
pusha
mov ch,0 ; calculate boxHeight
mov cl,[si+2]
sub cl,[si]
dec cl
mov boxHeight,cx
mov cx,boxHeight
call draw_side ; draw left side of box
mov cx,boxWidth
mov al,[si+2] ; lower-left row
mov row,al
mov al,[si+1] ; lower-left column
mov col,al
call draw_bottom ; draw bottom of box
popa
ret
draw_box endp
draw_side proc
mov al,row ; save the row
push ax
DS1:
mov al,vbar
call Outchar
inc row
loop DS1
draw_top proc
mov al,col ; save the column
push ax
mov cx,boxWidth
L2:
mov al,hbar ; horizontal bar char
call Outchar
inc col
loop L2
ret
draw_top endp
draw_bottom proc
mov al,col ; save the column
push ax
mov cx,boxWidth
DB1:
mov al,hbar
call Outchar
inc col
loop DB1
mov al,lrcorner
call Outchar
Outchar proc
push dx
mov ah,attribute
mov dh,row
mov dl,col
call Writechar_direct
pop dx
ret
Outchar endp
end main
.model small
.stack 100h
.286
whiteOnBlack = 0Fh
blueOnWhite = 71h
yellowOnBlue = 1Eh
magentaOnBlack = 0Dh
yellowOnBrown = 6Eh
TABLE_ENTRY_SIZE = 5
.data
; Format for table entry: top row, left column,
; bottom row, right column, attribute.
boxWidth dw ?
boxHeight dw ?
attribute db ? ; color of box frame
row db ?
col db ?
.code
include library.inc
main proc
mov ax,@data
mov ds,ax
mov si,offset boxes ; point to box table
call Clrscr
L1:
cmp byte ptr [si],0FFh ; end of table?
je quit ; yes: quit
call draw_box ; SI points to box information
add si,TABLE_ENTRY_SIZE
62 IRVINE: SOLUTIONS MANUAL
jmp L1
quit:
mov ax,4C00h ; return to DOS
int 21h
main endp
draw_box proc
pusha
mov ch,0 ; calculate boxHeight
mov cl,[si+2]
sub cl,[si]
dec cl
mov boxHeight,cx
mov cx,boxHeight
call draw_side ; draw left side of box
mov cx,boxWidth
mov al,[si+2] ; lower-left row
mov row,al
mov al,[si+1] ; lower-left column
mov col,al
call draw_bottom ; draw bottom of box
popa
ret
draw_box endp
draw_side proc
mov al,row ; save the row
push ax
DS1:
mov al,vbar
call Outchar
inc row
loop DS1
draw_top proc
mov al,col ; save the column
push ax
mov cx,boxWidth
L2:
mov al,hbar ; horizontal bar char
call Outchar
inc col
loop L2
draw_bottom proc
mov al,col ; save the column
push ax
mov cx,boxWidth
DB1:
mov al,hbar
call Outchar
inc col
loop DB1
mov al,lrcorner
call Outchar
Outchar proc
push dx
mov ah,attribute
mov dh,row
mov dl,col
call Writechar_direct
pop dx
ret
Outchar endp
end main
.model small
.stack 100h
.data
solidMsg db "Solid cursor: ",0
topMsg db "Top-line cursor: ",0
normalMsg db "Normal cursor: ",0
.code
include library.inc
main proc
mov ax,@data
mov ds,ax
call solid_cursor
CHAPTER 5 65
call top_cursor
call default_cursor
mov ax,4C00h
int 21h
main endp
solid_cursor proc
mov dx,offset solidMsg
call Writestring
mov ah,1
mov ch,0
mov cl,7
int 10h
call getch
call Crlf
ret
solid_cursor endp
top_cursor proc
mov dx,offset topMsg
call Writestring
mov ah,1
mov ch,0
mov cl,1
int 10h
call getch
call Crlf
ret
top_cursor endp
default_cursor proc
mov dx,offset normalMsg
call Writestring
mov ah,1
mov ch,6
mov cl,7
int 10h
call getch
call Crlf
ret
default_cursor endp
getch proc
mov ah,1 ; wait for keystroke
int 21h
ret
getch endp
end main
.model small
.286
.stack 100h
.data
msg db '<<Press any key to continue...>>'
MSG_SIZE = ($ - msg)
blank db MSG_SIZE dup(' ')
row db 24
col db 79 - MSG_SIZE
.code
main proc
mov ax,@data ; init data segment
mov ds,ax
mov ah,0 ; set video mode to mode
mov al,7 ; 7, to enable blinking
int 10h
show_text proc
pusha
L1:
push cx ; save loop counter
mov ah,2 ; set cursor position
mov bh,0 ; video page 0
int 10h ; DX = row,col
popa
ret
show_text endp
end main
.model small
.stack 100h
.data
message db "Can you see this string? $"
.code
main proc
mov ax,@data ; init data segment
mov ds,ax
mov ah,9
mov dx,offset message
int 21h
.model small
.stack 100h
.data
month db ?
day db ?
year dw ?
saveMonth db ?
saveDay db ?
saveYear dw ?
DAYSIZE = ($ - dayNames)
db "Monday ",0
db "Tuesday ",0
db "Wednesday",0
db "Thursday ",0
db "Friday ",0
db "Saturday ",0
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
call SaveCurrentDate
call InputDate ; get date from user
mov ah,2Bh
mov cx,year
mov dh,month
mov dl,day
int 21h
cmp al,0
jne restoreDate ; quit if not successful
mov ah,2Ah
int 21h ; AL = day of week
mov dx,offset dayNames ; point to table
mov ah,0
cmp ax,0 ; Sunday?
je printName ; yes: print now
printName:
call Writestring
call Crlf
restoreDate:
call RestoreCurrentDate
mov ax,4c00h ; end program
int 21h
main endp
RestoreCurrentDate proc
mov ah,2Bh
mov cx,saveYear
70 IRVINE: SOLUTIONS MANUAL
mov dh,saveMonth
mov dl,saveDay
int 21h
ret
RestoreCurrentDate endp
SaveCurrentDate proc
mov ah,2Ah
int 21h ; AL = day of week
mov saveMonth,dh
mov saveDay,dl
mov saveYear,cx
ret
SaveCurrentDate endp
InputDate proc
mov dx,offset askMonth
call Writestring
call Readint
call Crlf
mov month,al
mov dx,offset askDay
call Writestring
call Readint
call Crlf
mov day,al
mov dx,offset askYear
call Writestring
call Readint
call Crlf
mov year,ax
ret
InputDate endp
end main
; Challenging program.
; This is the solutin to an exercise from Chapter 5 in the First
; Edition. Solution by Bob Galivan.
.Model small
.Stack 100h
.Code
Main Proc
CHAPTER 5 71
ClearScreen:
mov ah, 07h ; calling BIOS function 7, scroll window down
mov al, 00h ; blanking the entire screen
mov bh, 07h ; in normal video
mov cx, 0000h ; row 1, col 1 start
mov dx, 184Fh ; row 25, col 80 finish
int 10h
PlaceCursor:
mov ah, 02h ; calling BIOS function 2, set cursor position
mov bh, 00h ; on video page 0
mov dx, 0000h ; at row 1, col 1
int 10h
ReadCharacter:
mov ah, 07h ; calling DOS function 8, character input
int 21h ; without echo
EvaluateCharacter:
cmp al, 'q' ; time to quit program
jne CheckExtendChar ; if user enters q
jmp Exit
CheckExtendChar:
cmp al, 00h ; if al = 0, an extended key was pressed
je GetExtendChar ; If not, it was an invalid key
jmp return ; and we cycle around again
GetExtendChar:
int 21h ; do a second read to get the character
CheckLeftArrow:
cmp al, LArrow ; was the key the left arrow key
jne CheckRightArrow ; if not, check the next key
mov cl, direction ; put the direction we are coming from into cl
mov direction, 'l' ; and set the direction to left
cmp cl, 'l' ; If we are coming from the left
je DrawLeftLine ; then we continue to the left
cmp cl, 'u' ; If we were going up,
je LtDrawTopRight ; then we need a top right corner
cmp cl, 'd' ; if we were going down,
je LtDrawBottomRight ; then we need a bottom right corner
jmp DrawLeftLine ; If we get here, we were coming from the right
LtDrawBottomRight:
mov ah, 09h ; Calling BIOS function 9, write character
mov al, BottomRight ; and attribute. We write the bottom right
mov bh, 00h ; character on vidoe page 0, with a normal
mov bl, 07h ; attribute,
mov cx, 01h ; one time
int 10h
jmp SetLeftCursor ; and then position the left cursor
LtDrawTopRight:
72 IRVINE: SOLUTIONS MANUAL
DrawLeftLine:
mov ah, 09h ; calling BIOS function 9, write character and
mov al, HorizLine ; attribute. We are writing a horizontal line
mov bh, 00h ; on vidoe page 0
mov bl, 07h ; with a normal attribute,
mov cx, 01h ; one character
int 10h
SetLeftCursor:
mov cl, MinCol ; Put the minimum column into cl
cmp CurrentCol,cl ; If we are below the minimum column,
ja PositionLeftCursor ; we cannot go further left. Otherwise
jmp PositionCursor ; we move one column to the left
PositionLeftCursor:
dec CurrentCol ; by decrementing the current column
jmp PositionCursor ; and positioning the cursor
CheckRightArrow:
cmp al, RArrow ; Was the key a right arrow ?
jne CheckUpArrow ; if not, check the next key
CheckRightDirection:
mov cl, Direction ; put the direction we are coming from
mov direction, 'r' ; into cl, and set the new direction to right
cmp cl, 'r' ; are we travelling right ?
je DrawRight ; If so, draw the line to the right
cmp cl, 'u' ; were we going up ?
je RtDrawTopLeft ; then we draw the top left corner
cmp cl, 'd' ; if we were going down,
je RtDrawBottomLeft ; then we need the bottom left corner
cmp cl, ' ' ; A space means we have just started
jmp RtDrawTopLeft ; so we draw a top left corner
RtDrawBottomLeft:
mov ah, 09h ; calling BIOS function 9, write character
mov al, BottomLeft ; and attribute. We write a bottom left character
mov bh, 00h ; on video page 0
mov bl, 07h ; with a normal attribute,
mov cx, 01h ; one character
int 10h
jmp SetRightCursor ; and reset the cursor
RtDrawTopLeft:
mov ah, 09h ; calling BIOS function 9, write character and
mov al, TopLeft ; attribute. Writing a Top left character
mov bh, 00h ; on video page 0
mov bl, 07h ; with a normal attribute,
mov cx, 01h ; one character
CHAPTER 5 73
int 10h
jmp SetRightCursor ; and reset the cursor
DrawRight:
mov ah, 09h ; If we got here, it is ok to write
mov al, HorizLine ; the horizontal line character
mov bh, 00h ; on video page 0
mov bl, 07h ; with a normal attribute,
mov cx, 01h ; write one character
int 10h
SetRightCursor:
mov cl, MaxCol ; put the maximum columns in cl to compare
cmp CurrentCol, cl ; If we are below the maximum column,
jb PositionRightCursor ; we can reset the cursor
jmp PositionCursor ; otherwise, we can go no further right
PositionRightCursor:
inc CurrentCol ; increment the current column
jmp PositionCursor ; and move the cursor one column to the right
CheckUpArrow:
cmp al, UpArrow ; was the key an Up arrow ?
jne CheckDnArrow ; If not, then we check the next key
mov cl, direction ; save the direction we came from
mov direction, 'u' ; and set it to where we are going
cmp cl, 'u' ; were we going up ?
je DrawUpLine ; then continue drawing
cmp cl, 'r' ; if we were coming right, then
je UpDrawBottomRight ; then we need a bottom right corner
cmp cl, 'l' ; if we were going left,
je UpDrawBottomLeft ; then we need the bottom left corner
UpDrawBottomLeft:
mov ah, 09h ; calling BIOS function 9, write character
mov al, BottomLeft ; and attribute. Writing the bottom left corner
mov bh, 00h ; on video page 0
mov bl, 07h ; with a normal attribute
mov cx, 01h ; write one character
int 10h
jmp SetUpCursor ; and resetting the cursor
UpDrawBottomRight:
mov ah, 09h ; calling BIOS function 9, write character and
mov al, BottomRight ; attribute. Writing the bottom right corner
mov bh, 00h ; on video page 0
mov bl, 07h ; with a normal attribute
mov cx, 01h ; write one character
int 10h
jmp SetUpCursor
DrawUpLine:
mov ah, 09h ; It is OK to write the vertical
mov al, VertLine ; line character
mov bh, 00h ; on video page 0
mov bl, 07h ; with a normal attribute
mov cx, 01h ; write one character
74 IRVINE: SOLUTIONS MANUAL
int 10h
SetUpCursor:
mov cl, MinRow ; put the minimum rows in cl
cmp CurrentRow, cl ; have we reached the minimum ?
jbe return ; if so, there is nothing else to do
dec CurrentRow ; otherwise, we decrement our row
jmp PositionCursor ; and reposition the cursor
CheckDnArrow:
cmp al, DnArrow ; was the key the down arrow ?
jne return ; If not, then we return to the top of the loop
mov cl, direction ; Else, we put the direction we are coming from
mov direction, 'd' ; into cl, and set the new direction.
cmp cl, 'd' ; are we still going down ?
je DrawDownLine ; if so, continue
cmp cl, 'r' ; If we were coming from the right
je DnDrawTopRight ; then we need a top right corner
cmp cl, 'l' ; If we were coming from the left
je DnDrawTopLeft ; then we need a top left corner
DnDrawTopLeft:
mov ah, 09h ; calling BIOS function 9, write character and
mov al, TopLeft ; attribute. Writing a top left corner
mov bh, 00h ; on video page 0
mov bl, 07h ; with a normal attribute
mov cx, 01h ; write one character
int 10h
jmp SetDownCursor ; and reseting the cursor
DnDrawTopRight:
mov ah, 09h ; calling BIOS function 9, write character and
mov al, TopRight ; attribute. Writing a top right corner
mov bh, 00h ; on video page 0
mov bl, 07h ; with a normal attribute
mov cx, 01h ; write one character
int 10h
jmp SetDownCursor ; and resetting the cursor
DrawDownLine:
mov ah, 09h ; It is OK to write the vertical
mov al, VertLine ; line character
mov bh, 00h ; on video page 0
mov bl, 07h ; with a normal attribute
mov cx, 01h ; write one character
int 10h
SetDownCursor:
mov cl, MaxRow ; put the maximum rows in cl
cmp CurrentRow, cl ; if we have exceeded them, we
jae return ; return to the top of the loop
inc CurrentRow ; otherwise, we increment the current row
jmp PositionCursor ; and position the cursor
PositionCursor:
mov ah, 02h ; Calling BIOS function 2, set cursor position
CHAPTER 6 75
Return:
jmp ReadCharacter ; return to the top of the loop for another
; round
Exit:
mov ax, 4C00h ; load the terminate function
Int 21h ; and end the program
Main Endp
.data
currentRow db 00h ; What is our current row
CurrentCol db 00h ; What is our current column
MinRow db 00h ; what is our minimum row
MinCol db 00h ; and column position
MaxRow db 18h ; and our maximum row
MaxCol db 4Fh ; and column position
Direction db ' ' ; What direction are we going in
;Character definitions
UpArrow db 48h
DnArrow db 50h
LArrow db 4Bh
RArrow db 4Dh
TopLeft db 0DAh
TopRight db 0BFh
BottomLeft db 0C0h
BottomRight db 0D9h
HorizLine db 0C4h
VertLine db 0B3h
end Main
CHAPTER 6
;--------------------------------------------------------------------
; Suggested improvement (after reading chapter 7): Create a table of
; strings representing the key names, and index into the table to
; get the appropriate string. This requires the MUL operator:
;keyNames \
; db "None ",0
; db "Up arrow ",0
; db "Right arrow",0
76 IRVINE: SOLUTIONS MANUAL
.model small
.stack 100h
LEFT = 4Bh
RIGHT = 4Dh
UP = 48h
DOWN = 50h
NONE = 0
.data
rightMsg db "Right arrow",0
leftMsg db "Left arrow",0
upMsg db "Up arrow",0
downMsg db "Down arrow",0
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
Quit:
mov ax,4c00h ; end program
int 21h
main endp
GetArrow proc
push dx
mov ah,10h ; input keystroke (AH = scan code)
int 16h
mov dl,ah ; make copy of scan code
cmp dl,UP
jne GA1
mov ax,1
mov dx,offset upMsg
call Writestring
jmp GA5
GA1:
cmp dl,RIGHT
CHAPTER 6 77
jne GA2
mov ax,2
mov dx,offset rightMsg
call Writestring
jmp GA5
GA2:
cmp dl,DOWN
jne GA3
mov ax,3
mov dx,offset downMsg
call Writestring
jmp GA5
GA3:
cmp dl,LEFT
jne GA4
mov ax,4
mov dx,offset leftMsg
call Writestring
jmp GA5
GA4:
mov ax,0 ; unknown key
GA5:
call Crlf
pop dx
ret
GetArrow endp
end main
.model small
.286
.stack 100h
ESC_KEY = 1Bh
UP_ARROW = 48h
DOWN_ARROW = 50h
78 IRVINE: SOLUTIONS MANUAL
.data
screenPosition db 5,10 ; upper-left row, column
numberOfEntries dw 3 ; number of entries
unselectedColor db 70h ; color of unselected entries
selectedColor db 20h ; color of selected entries
menuText \
db "Choice One ",0
ENTRY_SIZE = ($ - menuText)
db "Choice Two ",0
db "Choice Three ",0
currentRow dw 0
prompt db "Press the Esc key to cancel the menu.",0
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
mov dx,offset prompt
call Writestring
call DrawVerticalMenu
mov currentRow,0
L1:
call ProcessVerticalKey
cmp al,ESC_KEY
jne L1
; Read the keyboard and move the menu bar according to the
; vertical arrow key that was pressed. Exit if the user presses
; the Esc key
ProcessVerticalKey proc
mov ah,10h ; read keyboard
int 16h
cmp al,ESC_KEY ; exit if Esc pressed
je PK4
cmp ah,UP_ARROW
jne PK2
cmp currentRow,0 ; if currentRow > 0 then
jbe PK2
mov ah,unselectedColor ; unselect the current entry
call DrawEntry
dec currentRow ; decrement the row number
mov ah,selectedColor ; select the new entry
call DrawEntry
CHAPTER 6 79
PK2:
cmp ah,DOWN_ARROW
jne PK4
mov dx,numberOfEntries ; get number of entries
dec dx ; adjust to zero-based index
cmp dx,currentRow ; any rows below currentRow?
jbe PK4 ; if so,
PK4:
ret
ProcessVerticalKey endp
DrawVerticalMenu proc
pusha
mov di,offset selectedColor
mov ah,[di] ; selected attribute
mov currentRow,0
call DrawEntry ; draw the first entry
DM1:
inc currentRow
call DrawEntry
loop DM1
popa
ret
DrawVerticalMenu endp
DrawEntry proc
pusha
mov cx,currentRow
mov di,offset screenPosition
mov dh,[di] ; starting screen row
mov dl,[di+1] ; starting screen column
mov si,offset menuText
cmp cx,0
je DE2
DE1:
inc dh
add si,ENTRY_SIZE ; next text entry
loop DE1
DE2:
call Writestring_direct ; display current entry
popa
ret
DrawEntry endp
end main
.model small
.286
.stack 100h
ESC_KEY = 1Bh
LEFT_ARROW = 4Bh
RIGHT_ARROW = 4Dh
.data
screenPosition db 5,0 ; upper-left row, column
numberOfEntries dw 3 ; number of entries
unselectedColor db 70h ; color of unselected entries
selectedColor db 20h ; color of selected entries
menuText \
db "ChoiceOne ",0
ENTRY_SIZE = ($ - menuText)
db "ChoiceTwo ",0
db "ChoiceThree ",0
currentCol dw 0
prompt db "Press the Esc key to cancel the menu.",0
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
CHAPTER 6 81
call DrawHorizontalMenu
mov currentCol,0
L1:
call ProcessHorizontalKey
cmp al,ESC_KEY
jne L1
; Read the keyboard and move the menu bar according to the
; horizontal arrow key that was pressed. Exit if the user presses
; the Esc key
ProcessHorizontalKey proc
mov ah,10h ; read keyboard
int 16h
cmp al,ESC_KEY ; exit if Esc pressed
je PK4
cmp ah,LEFT_ARROW
jne PK2
cmp currentCol,0 ; if currentCol > 0 then
jbe PK2
mov ah,unselectedColor ; unselect the current entry
call DrawEntry
dec currentCol ; decrement the column number
mov ah,selectedColor ; select the new entry
call DrawEntry
PK2:
cmp ah,RIGHT_ARROW
jne PK4
PK4:
ret
ProcessHorizontalKey endp
DrawHorizontalMenu proc
pusha
82 IRVINE: SOLUTIONS MANUAL
DM1:
inc currentCol
call DrawEntry
loop DM1
popa
ret
DrawHorizontalMenu endp
DrawEntry proc
pusha
mov cx,currentCol
mov di,offset screenPosition
mov dh,[di] ; starting screen row
mov dl,[di+1] ; starting screen column
mov si,offset menuText
cmp cx,0
je DE2
DE1:
add dl,ENTRY_SIZE ; screen column
add si,ENTRY_SIZE ; next text entry
loop DE1
DE2:
call Writestring_direct ; display current entry
popa
ret
DrawEntry endp
end main
.model small
.stack 100h
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
L2: jmp L1
Quit:
mov ax,4c00h ; end program
int 21h
main endp
end main
.model small
.stack 100h
DOS_CHAR_INPUT = 1
ENTER_KEY = 0Dh
.data
InvalidInputMessage db "Invalid input",0dh,0ah,0
.code
extrn Writestring:proc, Crlf:proc, Clrscr:proc
main proc
mov ax,@data
84 IRVINE: SOLUTIONS MANUAL
mov ds,ax
call Clrscr
StateA:
call Getnext ; read next char into AL
cmp al,'+' ; leading + sign?
je StateB ; go to State B
cmp al,'-' ; leading - sign?
je StateB ; go to State C
call Isdigit ; ZF = 1 if AL contains a digit
jz StateC
call DisplayError ; invalid input found
jmp Exit
StateB:
call Getnext ; read next char into AL
call Isdigit ; ZF = 1 if AL contains a digit
jz StateC
call DisplayError ; invalid input found
jmp Exit
StateC:
call Getnext ; read next char into AL
jz Exit ; quit if Enter pressed
call Isdigit ; ZF = 1 if AL contains a digit
jz StateC
call DisplayError ; invalid input found
jmp Exit
Exit:
call Crlf
mov ax,4c00h
int 21h
main endp
Getnext proc
mov ah,DOS_CHAR_INPUT ; read standard input
int 21h ; AL = character
cmp al,ENTER_KEY
ret
Getnext endp
Isdigit proc
cmp al,'0'
jb A1
cmp al,'9'
ja A1
test ax,0 ; set ZF = 1
A1: ret
Isdigit endp
CHAPTER 6 85
DisplayError proc
push dx
mov dx,offset InvalidInputMessage
call WriteString
call Crlf
pop dx
ret
DisplayError endp
end main
.model small
.stack 100h
ENTER_KEY = 0Dh
.data
prompt db "Input a real number: ",0
.code
include library.inc
main proc
mov ax,@data
mov ds,ax
call Clrscr
mov dx,offset prompt ; display a prompt
call Writestring
StateA:
call Readkey ; read character into AL
cmp al,ENTER_KEY
je quit
call IsDigit
jz StateB
StateB:
call Readkey ; read next char into AL
cmp al,ENTER_KEY
je quit ; quit if Enter pressed
call IsDigit ; ZF = 1 if AL contains a digit
jz StateB
call IsPoint
je StateC
StateC:
86 IRVINE: SOLUTIONS MANUAL
quit:
mov ax,4C00h ; end program
int 21h
main endp
IsDigit proc
cmp al,'0' ; is the ASCII code < '0'?
jb ID1
cmp al,'9' ; is the ASCII code > '0'?
ja ID1
call Echo1
test ax,0 ; set ZF = 1
ID1: ret
IsDigit endp
IsPoint proc
cmp al,'.'
jne IP1
call Echo1
test ax,0
IP1: ret
IsPoint endp
Echo1 proc
mov dl,al ; echo the character in AL
mov ah,2
int 21h
ret
Echo1 endp
end main
.model small
.stack 100h
ENTER_KEY = 0Dh
.data
prompt db "Input a real number: ",0
.code
include library.inc
main proc
mov ax,@data
mov ds,ax
call Clrscr
mov dx,offset prompt ; display a prompt
call Writestring
StateA:
call Readkey
cmp al,ENTER_KEY
je quit
call IsSign
jz StateB
call IsDigit
jz StateC
jmp StateA ; ignore other characters
StateB:
call Readkey ; read character into AL
cmp al,ENTER_KEY
je quit
call IsDigit
jz StateC
jmp StateB ; ignore other characters
StateC:
call Readkey ; read next char into AL
cmp al,ENTER_KEY
je quit ; quit if Enter pressed
call IsDigit ; ZF = 1 if AL contains a digit
jz StateC
call IsPoint
je StateD
jmp StateC ; ignore other characters
StateD:
call Readkey ; read next char into AL
cmp al,ENTER_KEY
je quit ; quit if Enter pressed
call Isdigit ; ZF = 1 if AL contains a digit
jz StateD ; repeat for more digits
jmp StateD ; ignore other characters
quit:
mov ax,4C00h ; end program
88 IRVINE: SOLUTIONS MANUAL
int 21h
main endp
IsDigit proc
cmp al,'0' ; is the ASCII code < '0'?
jb ID1
cmp al,'9' ; is the ASCII code > '0'?
ja ID1
call Echo1
test ax,0 ; set ZF = 1
ID1: ret
IsDigit endp
IsPoint proc
cmp al,'.'
jne IP1
call Echo1
test ax,0
IP1: ret
IsPoint endp
IsSign proc
cmp al,'+'
je IS1
cmp al,'-'
je IS1
jmp IS2
IS2: ret
IsSign endp
Echo1 proc
mov dl,al ; echo the character in AL
mov ah,2
int 21h
ret
Echo1 endp
end main
.model small
CHAPTER 6 89
.stack 100h
.data
array dw 1,2,3,4,5,6,7,8,9,10
COUNT = ($ - array) / (type array)
array_last = $ - (type array)
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
call ShowArray
mov cx,(COUNT / 2)
mov si,offset array
mov di,array_last
L1:
mov ax,[si] ; get integer from the source
xchg [di],ax ; exchange with destination
mov [si],ax ; save back in source
add si,type array ; increment index
sub di,type array
loop L1 ; repeat loop
call ShowArray
ShowArray proc
mov si,offset array
mov cx,COUNT
SA1:
mov ax,[si]
mov bx,10 ; display it in decimal
call Writeint
call Crlf
add si,type array ; increment index
loop SA1
ret
ShowArray endp
end main
.model small
.stack 100h
.386
.data
array dw 1000,1100,2000,2200,3000,3300,4000,4400
dw 5000,5500,6000,6600,7000,7700,8000,8800
COUNT = ($ - array) / (type array)
mean dd ?
showMeanMsg db "Arithmetic mean (integer): ",0
heading db 0dh,0ah
db "Value - Variance",0dh,0ah
db "----------------",0dh,0ah,0
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
call CalculateMean
mov cx,COUNT
mov eax,0 ; use 32-bit accumulator
mov si,offset array
L1:
mov ax,[si] ; get array value
call Writeint ; display it
call Writespaces
sub ax,word ptr mean ; subtract the mean
jns L2 ; result negative?
neg ax ; convert to positive
CHAPTER 6 91
L2:
call Writeint ; display the difference
call Crlf
add si,type array ; point to next array value
loop L1
CalculateMean proc
pusha
mov cx,COUNT
mov eax,0 ; use 32-bit accumulator
mov si,offset array
CM1:
movzx edx,word ptr[si] ; move and zero-extend
add eax,edx ; add to accumulator
add si,type array
loop CM1
mov edx,0
mov ebx,COUNT
div ebx ; NEEDS EXPLANATION
mov mean,eax
popa
ret
CalculateMean endp
Writespaces proc
.data
tspaces db " - ",0
.code
push dx
mov dx,offset tspaces
call Writestring
pop dx
ret
Writespaces endp
end main
.model small
.stack 100h
.data
string db 128 dup(0) ; the input string
92 IRVINE: SOLUTIONS MANUAL
.code
include library.inc
main proc
mov ax,@data
mov ds,ax
call Clrscr
mov ax,4c00h
int 21h
main endp
ReverseString proc
mov cx,ax ; loop counter
mov si,dx ; point to string
add si,ax ; point to last char
dec si
RS1:
mov dl,[si] ; get character
cmp dl,0
je RS2
mov ah,2 ; display it
int 21h
dec si
loop RS1
RS2:
ret
ReverseString endp
end main
.model small
.stack 100h
.data
buffer db 81 ; maximum length as specified in
book
db ? ; length of data keyed
string db 81 dup (?) ; the input string
crlf db 0Dh,0Ah,'$'
CHAPTER 6 93
.code
main proc
mov ax,@data ; establish segment register
contents
mov ds,ax ; so that data may be accessed
call ReverseString ; Call main routine
mov ax,4c00h
int 21h ; Exit the program
main endp
ReverseString proc
call get_string ; get a string, set si to the end
or si,si ; if ZF, then no data was entered
jz zero_length ; can't reverse a zero length
string
next_word:
mov cx,0 ; set length of word to zero
again:
cmp string-1[si], 20h ; have we reached a space ?
je a_word ; word ends in one or more spaces
inc cx ; add 1 to length of the word
dec si ; move toward the front of the
string
jz done ; until we reach the very
beginning
jmp again ; check the next character
a_word:
call show_word ; display a word
mov cx,1 ; we know we have a space
sep_len:
dec si ; move towards beginning of string
jz done ; until we reach the very
beginning
cmp string-1[si], 20h ; is the seperator over yet?
jne sep_done ; if not, print it
inc cx ; and increase the length counter
jmp sep_len ; keep looking for a space
sep_done:
call show_word ; don't care if word or separator
jmp next_word ; next word
done: call show_word ; display final word
zero_length:
ret
ReverseString endp
int 21h
ret ; return to calling routine
get_string endp
.model small
.stack 100h
.data
charCount dw 0 ; character count
wordCount dw 0 ; word count
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
jne L2 ; no
inc wordCount ; yes - increment count
L2:
jmp L1
L8:
inc wordCount ; count the last word
.model small
.286
.stack 100h
.data
96 IRVINE: SOLUTIONS MANUAL
field1Length = 10
field1 \
dw field1Length
db 5,12 ; row, column position
db 70h ; attribute
db ? ; count actual chars typed
db field1Length dup(0),0
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
ENTER_KEY = 0Dh
TAB_KEY = 9
BKSP_KEY = 8
FILL_CHAR = '.'
ShowInputField proc
pusha
SIF2:
mov ah,8 ; get keystroke, no echo
int 21h ; AL = ASCII code
cmp al,0 ; extended key?
jne SIF2A ; no
int 21h ; yes - read extra kbyd byte
jmp SIF2 ; look for another key
SIF2A:
call CheckCharacter ; echo normal characters
cmp al,ENTER_KEY ; look for exit keys
je SIF9
cmp al,TAB_KEY
je SIF9
cmp al,BKSP_KEY ; look for backspace
je SIF3
mov [di],al ; save the key
inc di
inc cx ; add to character count
cmp cx,[si] ; max characters reached?
jb SIF2
jmp SIF9 ; exit if not
SIF3:
cmp cx,0 ; first keystroke?
je SIF2 ; yes: get another keystroke
mov dx,offset EraseString ; erase the previous char
call Writestring
dec di ; back up in the buffer
dec cx ; decrement character count
jmp SIF2 ; get another keystroke
SIF9:
mov [si+6],cx ; save buffer count
popa
ret
.data
EraseString db BKSP_KEY, FILL_CHAR, BKSP_KEY,0
.code
ShowInputField endp
CheckCharacter proc
push di
push dx
98 IRVINE: SOLUTIONS MANUAL
CF1:
cmp al,ENTER_KEY ; don't echo the Enter key
je CF2
cmp al,TAB_KEY ; don't echo the Tab key
je CF2
cmp al,BKSP_KEY ; don't echo the Bksp key
je CF2
call EchoKey
CF2:
pop dx
pop di
ret
CheckCharacter endp
EchoKey proc
push ax
push dx
mov ah,2
mov dl,al
int 21h
pop dx
pop ax
ret
EchoKey endp
end main
.model small
.286
.stack 100h
ENTER_KEY = 0Dh
TAB_KEY = 9
BKSP_KEY = 8
.data
CHAPTER 6 99
field1Length = 10
field1 \
dw field1Length
db 5,12 ; row, column position
db 70h ; attribute
db ? ; count actual chars typed
dw field1Filter ; pointer to filter string
db field1Length dup(0),0
field1Filter db "0123456789",TAB_KEY,BKSP_KEY,ENTER_KEY,0
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
FILL_CHAR = '.'
ShowInputField proc
pusha
SIF2:
mov ah,8 ; get keystroke, no echo
int 21h ; AL = ASCII code
cmp al,0 ; extended key?
jne SIF2A ; no
int 21h ; yes - read extra kbyd byte
jmp SIF2 ; look for another key
SIF2A:
call CheckFilter ; check for valid characters
cmp al,0 ; if AL = 0, then
je SIF2 ; skip this character
cmp al,ENTER_KEY ; look for exit keys
je SIF9
cmp al,TAB_KEY
je SIF9
cmp al,BKSP_KEY ; look for backspace
je SIF3
mov [di],al ; save the key
inc di
inc cx ; add to character count
cmp cx,[si] ; max characters reached?
jb SIF2
jmp SIF9 ; exit if not
SIF3:
cmp cx,0 ; first keystroke?
je SIF2 ; yes: get another keystroke
mov dx,offset EraseString ; erase the previous char
call Writestring
dec di ; back up in the buffer
dec cx ; decrement character count
jmp SIF2 ; get another keystroke
SIF9:
mov [si+6],cx ; save buffer count
popa
ret
.data
EraseString db BKSP_KEY, FILL_CHAR, BKSP_KEY,0
.code
ShowInputField endp
CheckFilter proc
push di
push dx
CF1:
cmp al,ENTER_KEY ; don't echo the Enter key
je CF2
cmp al,TAB_KEY ; don't echo the Tab key
je CF2
cmp al,BKSP_KEY ; don't echo the Bksp key
je CF2
call EchoKey
test al,0 ; set ZF = 1
CF2:
pop dx
pop di
ret
CheckFilter endp
FindInString proc
pusha
FIS1:
cmp [di],0 ; end of string?
je FIS2 ; if so, quit with ZF = 0
cmp [di],al ; check current character
je FIS3 ; if match found, exit with ZF = 1
inc di ; move to next character
jmp FIS1
FIS2:
or al,1 ; clear ZF = 0
FIS3:
popa
ret
FindInString endp
EchoKey proc
push ax
102 IRVINE: SOLUTIONS MANUAL
push dx
mov ah,2
mov dl,al
int 21h
pop dx
pop ax
ret
EchoKey endp
end main
.model small
.286
.stack 100h
.data
ScreenTitle db 'ACCOUNT INPUT SCREEN',0
AcctTitle db ' ACCT NUM:',0
NameTitle db ' LAST NAME:',0
PrevBalTitle db 'PREV BALANCE:',0
PymtsTitle db ' PAYMENTS:',0
CrTitle db ' CREDITS:',0
StartOfPrompts \
dw 0419h ; this section contains both
dw offset ScreenTitle ; screen positions for prompts
dw 060Fh ; and the addresses of the prompts
dw offset NameTitle ; The first entry is the row and
dw 080Fh ; column position for the first
dw offset AcctTitle ; prompt. The second entry is the
dw 0A0Fh ; address of the first prompt.
dw offset PrevBalTitle ; the display routine goes through
dw 0C0Fh ; this portion of the data, using
dw offset PymtsTitle ; these values to write to the
dw 0E0Fh ; screen
dw offset CrTitle
LastNameLen = 30
AcctNumLen = 6
PrevBalLen = 11
PaymentsLen = 11
CreditsLen = 11
LastName \
dw LastNameLen
db 6,29 ; row, column position
db 70h ; attribute
db ? ; count actual chars typed
db LastNameLen dup(0),0
CHAPTER 6 103
AcctNum \
dw AcctNumLen
db 8,29 ; row, column position
db 70h ; attribute
db ? ; count actual chars typed
db AcctNumLen dup(0),0
PrevBal \
dw PrevBalLen
db 10,29 ; row, column position
db 70h ; attribute
db ? ; count actual chars typed
db PrevBalLen dup(0),0
Payments \
dw PaymentsLen
db 12,29 ; row, column position
db 70h ; attribute
db ? ; count actual chars typed
db PaymentsLen dup(0),0
Credits \
dw CreditsLen
db 14,29 ; row, column position
db 70h ; attribute
db ? ; count actual chars typed
db CreditsLen dup(0),0
LineLength dw 50
HorizLine db 196
VertLine db 179
TopLeft db 218
Botleft db 192
TopRight db 191
BotRight db 217
.Code
include library.inc
main proc
mov ax, @data ; init data segment
mov ds, ax
call SetupScreen ; Draw the input form
call WritePrompts ; write prompts to the screen
call GetFormData ; Get the user input
Exit:
mov ax, 4C00h ; end program
int 21h
main endp
GetFormData proc
104 IRVINE: SOLUTIONS MANUAL
GF9:
ret
GetFormData endp
ENTER_KEY = 0Dh
TAB_KEY = 9
BKSP_KEY = 8
FILL_CHAR = '.'
ShowInputField proc
pusha
SIF2:
CHAPTER 6 105
SIF2A:
call CheckCharacter ; echo normal characters
cmp al,ENTER_KEY ; look for exit keys
je SIF8 ; end of Form
cmp al,TAB_KEY
je SIF9 ; end of Field
cmp al,BKSP_KEY ; look for backspace
je SIF3
mov [di],al ; save the key
inc di
inc cx ; add to character count
cmp cx,[si] ; max characters reached?
jb SIF2
jmp SIF9 ; end of Field
SIF3:
cmp cx,0 ; first keystroke?
je SIF2 ; yes: get another keystroke
mov dx,offset EraseString ; erase the previous char
call Writestring
dec di ; back up in the buffer
dec cx ; decrement character count
jmp SIF2 ; get another keystroke
SIF8:
stc ; signal end of form
jmp SIF10
SIF9:
clc ; signal end of field
SIF10:
mov [si+6],cx ; save buffer count
popa
ret
.data
EraseString db BKSP_KEY, FILL_CHAR, BKSP_KEY,0
.code
ShowInputField endp
CheckCharacter proc
push di
push dx
CF1:
cmp al,ENTER_KEY ; don't echo the Enter key
je CF2
cmp al,TAB_KEY ; don't echo the Tab key
je CF2
cmp al,BKSP_KEY ; don't echo the Bksp key
je CF2
call EchoKey
CF2:
pop dx
pop di
ret
CheckCharacter endp
EchoKey proc
push ax
push dx
mov ah,2
mov dl,al
int 21h
pop dx
pop ax
ret
EchoKey endp
;------------------------------------------------------------------
SetupScreen proc
push dx
call Clrscr
mov dx, 050Ah ; cursor will be at row 5, col 10
call PlaceCursor
call DrawHorizontal ; draw the top line of the box
call DrawVertical ; and the left side
mov dx, 0F0Ah ; move cursor to row 15, col 10
call PlaceCursor
call DrawHorizontal ; Draw the bottom line
mov dx, 053Ch ; Place the cursor at row 5, col 60
call DrawVertical ; and draw the right side
call DrawCorners
pop dx
ret
SetupScreen endp
PlaceCursor proc
mov ah, 02h ; BIOS function set cursor position
mov bh, 00h ; on video page 0
int 10h ; coordinates passed in dx
ret
PlaceCursor endp
DrawHorizontal proc
CHAPTER 6 107
DrawVertical proc
mov cx, 0Ah ; The box is 15 high
mov al, VertLine
mov bx, 0007 ; page 0 with a normal attribute
DrawIt:
push ax ; this routine draws a vertical line
push bx ; by incrementing the dh register,
push cx ; which contains the row at which the
mov cx, 1 ; character is to be written
int 10h ; the column in dl remains constant
inc dh ; The registers must be saved since
call PlaceCursor ; the same ones are used for drawing
pop cx ; and positioning the cursor
pop bx
pop ax
loop DrawIt
ret
DrawVertical endp
DrawOneChar proc
mov ah, 09h ; BIOS function write character
mov bx, 0007h ; video page 0 with normal attribute
mov cx, 1 ; only one character to write
int 10h
ret
DrawOneChar endp
DrawCorners proc
mov al, BotRight ; load the corner character
call DrawOneChar ; and draw it
mov dx, 050Ah ; move cursor to 5,10
call PlaceCursor
mov al, TopLeft ; Load the top left char
call DrawOneChar ; and draw it
mov dx, 053Ch ; move to top right corner
call PlaceCursor
mov al, TopRight ; and draw that character
call DrawOneChar
mov dx, 0F0Ah ; and move to the bottom left
call PlaceCursor
mov al, BotLeft ; and draw the corner
call DrawOneChar
ret
DrawCorners endp
WritePrompts proc
108 IRVINE: SOLUTIONS MANUAL
; Notes:
; This Exercise is easily the most difficult one to
; complete up to this point in the chapter. It requires
; careful debugging and should be a lot of fun for students
; who like challenges.
; ----------------------------------------------------------
; Another way to implement this solution program is to adapt
; the Finite State Machine program from Exercise 5 in this
CHAPTER 6 109
.model small
.286
.stack 100h
.data
ScreenTitle db 'ACCOUNT INPUT SCREEN',0
AcctTitle db ' ACCT NUM:',0
NameTitle db ' LAST NAME:',0
PrevBalTitle db 'PREV BALANCE:',0
PymtsTitle db ' PAYMENTS:',0
CrTitle db ' CREDITS:',0
StartOfPrompts \
dw 0419h ; this section contains both
dw offset ScreenTitle ; screen positions for prompts
dw 060Fh ; and the addresses of the prompts
dw offset NameTitle ; The first entry is the row and
dw 080Fh ; column position for the first
dw offset AcctTitle ; prompt. The second entry is the
dw 0A0Fh ; address of the first prompt.
dw offset PrevBalTitle ; the display routine goes through
dw 0C0Fh ; this portion of the data, using
dw offset PymtsTitle ; these values to write to the
dw 0E0Fh ; screen
dw offset CrTitle
LastNameLen = 30
AcctNumLen = 6
PrevBalLen = 11
PaymentsLen = 11
CreditsLen = 11
NO_FILTER = -1
LastName \
dw LastNameLen
db 6,29 ; row, column position
db 70h ; attribute
db ? ; count actual chars typed
FILTER_OFFSET = ($ - LastName)
dw NO_FILTER
BUFFER_OFFSET = ($ - LastName) ; used by ShowInputField
db LastNameLen dup(0),0
AcctNum \
dw AcctNumLen
db 8,29 ; row, column position
db 70h ; attribute
db ? ; count actual chars typed
dw NO_FILTER
db AcctNumLen dup(0),0
110 IRVINE: SOLUTIONS MANUAL
PrevBal \
dw PrevBalLen
db 10,29 ; row, column position
db 70h ; attribute
db ? ; count actual chars typed
dw SignedNumberFilter
db PrevBalLen dup(0),0
Payments \
dw PaymentsLen
db 12,29 ; row, column position
db 70h ; attribute
db ? ; count actual chars typed
dw SignedNumberFilter
db PaymentsLen dup(0),0
Credits \
dw CreditsLen
db 14,29 ; row, column position
db 70h ; attribute
db ? ; count actual chars typed
dw SignedNumberFilter
db CreditsLen dup(0),0
LineLength dw 50
HorizLine db 196
VertLine db 179
TopLeft db 218
Botleft db 192
TopRight db 191
BotRight db 217
.Code
include library.inc
main proc
mov ax, @data ; init data segment
mov ds, ax
call SetupScreen ; Draw the input form
call WritePrompts ; write prompts to the screen
call GetFormData ; Get the user input
Exit:
mov ax, 4C00h ; end program
int 21h
main endp
GetFormData proc
mov si,offset LastName
call ShowInputField
jc GF9
mov si,offset AcctNum
CHAPTER 6 111
call ShowInputField
jc GF9
mov si,offset PrevBal
call ShowInputField
jc GF9
mov si,offset Payments
call ShowInputField
jc GF9
mov si,offset Credits
call ShowInputField
jc GF9
GF9:
ret
GetFormData endp
ENTER_KEY = 0Dh
TAB_KEY = 9
BKSP_KEY = 8
FILL_CHAR = '.'
ShowInputField proc
pusha
mov decimalCount,0
SIF2:
mov ah,8 ; get keystroke, no echo
int 21h ; AL = ASCII code
cmp al,0 ; extended key?
112 IRVINE: SOLUTIONS MANUAL
jne SIF2A ; no
int 21h ; yes - read extra kbyd byte
jmp SIF2 ; look for another key
SIF2A:
cmp [si+FILTER_OFFSET],NO_FILTER ; filter procedure specified?
jne SIF2B ; yes: skip next two lines
call EchoKey ; no: echo the character
jmp SIF3 ; and continue
SIF2B:
call near ptr [si+FILTER_OFFSET] ; call filter procedure
jnz SIF2 ; invalid character if ZF = 0
SIF3:
cmp al,ENTER_KEY ; look for exit keys
je SIF8 ; end of Form
cmp al,TAB_KEY
je SIF9 ; end of Field
cmp al,BKSP_KEY ; look for backspace
je SIF5
mov [di],al ; save the key
inc di
inc cx ; add to character count
cmp cx,[si] ; max characters reached?
jb SIF2
jmp SIF9 ; end of Field
SIF5:
cmp cx,0 ; first keystroke?
je SIF2 ; yes: get another keystroke
mov dx,offset EraseString ; erase the previous char
call Writestring
dec di ; back up in the buffer
dec cx ; decrement character count
jmp SIF2 ; get another keystroke
SIF8:
stc ; signal end of form
jmp SIF10
SIF9:
clc ; signal end of field
SIF10:
mov [si+6],cx ; save buffer count
popa
ret
.data
EraseString db BKSP_KEY, FILL_CHAR, BKSP_KEY,0
.code
ShowInputField endp
SignedNumberFilter proc
pusha
cmp cx,0 ; first digit?
jne SNFA ; no
mov decimalCount,0 ; yes: init decimal count
SNFA:
mov di,offset ValidChars ; get filter string
call FindInString ; is AL in the string?
jnz SNF8 ; no, exit with beep
SNF3:
cmp al,ENTER_KEY ; don't echo the Enter key
je SNF9
cmp al,TAB_KEY ; don't echo the Tab key
je SNF9
cmp al,BKSP_KEY ; don't echo the Bksp key
je SNF9
call EchoKey
test al,0 ; set ZF = 1: Filter passed
jmp SNF9
SNF9:
popa
ret
.data
ValidChars db ".+-0123456789",TAB_KEY,BKSP_KEY,ENTER_KEY,0
decimalCount db ?
.code
114 IRVINE: SOLUTIONS MANUAL
SignedNumberFilter endp
FindInString proc
pusha
FIS1:
cmp [di],0 ; end of string?
je FIS2 ; if so, quit with ZF = 0
cmp [di],al ; check current character
je FIS3 ; if match found, exit with ZF = 1
inc di ; move to next character
jmp FIS1
FIS2:
or al,1 ; clear ZF = 0
FIS3:
popa
ret
FindInString endp
EchoKey proc
push ax
push dx
mov ah,2
mov dl,al
int 21h
pop dx
pop ax
ret
EchoKey endp
;------------------------------------------------------------------
SetupScreen proc
push dx
call Clrscr
mov dx, 050Ah ; cursor will be at row 5, col 10
call PlaceCursor
call DrawHorizontal ; draw the top line of the box
call DrawVertical ; and the left side
mov dx, 0F0Ah ; move cursor to row 15, col 10
call PlaceCursor
call DrawHorizontal ; Draw the bottom line
mov dx, 053Ch ; Place the cursor at row 5, col 60
call DrawVertical ; and draw the right side
call DrawCorners
pop dx
ret
SetupScreen endp
CHAPTER 6 115
PlaceCursor proc
mov ah, 02h ; BIOS function set cursor position
mov bh, 00h ; on video page 0
int 10h ; coordinates passed in dx
ret
PlaceCursor endp
DrawHorizontal proc
mov ah, 09h ; BIOS function write character
mov al, horizLine ; the horizontal line
mov bh, 00h ; on video page 0
mov bl, 07h ; with a normal attribute
mov cx, LineLength ; and size of the line
int 10h
ret
DrawHorizontal endp
DrawVertical proc
mov cx, 0Ah ; The box is 15 high
mov al, VertLine
mov bx, 0007 ; page 0 with a normal attribute
DrawIt:
push ax ; this routine draws a vertical line
push bx ; by incrementing the dh register,
push cx ; which contains the row at which the
mov cx, 1 ; character is to be written
int 10h ; the column in dl remains constant
inc dh ; The registers must be saved since
call PlaceCursor ; the same ones are used for drawing
pop cx ; and positioning the cursor
pop bx
pop ax
loop DrawIt
ret
DrawVertical endp
DrawOneChar proc
mov ah, 09h ; BIOS function write character
mov bx, 0007h ; video page 0 with normal attribute
mov cx, 1 ; only one character to write
int 10h
ret
DrawOneChar endp
DrawCorners proc
mov al, BotRight ; load the corner character
call DrawOneChar ; and draw it
mov dx, 050Ah ; move cursor to 5,10
call PlaceCursor
mov al, TopLeft ; Load the top left char
call DrawOneChar ; and draw it
mov dx, 053Ch ; move to top right corner
call PlaceCursor
mov al, TopRight ; and draw that character
116 IRVINE: SOLUTIONS MANUAL
call DrawOneChar
mov dx, 0F0Ah ; and move to the bottom left
call PlaceCursor
mov al, BotLeft ; and draw the corner
call DrawOneChar
ret
DrawCorners endp
WritePrompts proc
mov cx, 6 ; number of prompts to write
mov bx, offset StartOfPrompts ; and the beginning of the data
WriteLoop:
mov dx, [bx] ; loads the cursor position into dx
call PlaceCursor ; and places it
add bx, 2 ; move bx to the next table entry
mov dx, [bx] ; which is the address of the prompt
call Writestring ; load it into dx, and write it out
add bx, 2 ; move bx to the next entry, which is
loop WriteLoop ; a cursor position, and repeat.
ret
WritePrompts endp
end main
; Notes:
; This Exercise is easily the most difficult one to
; complete up to this point in the chapter. It requires
; careful debugging and should be a lot of fun for students
; who like challenges.
; ----------------------------------------------------------
; Another way to implement this solution program is to adapt
; the Finite State Machine program from Exercise 5 in this
; chapter to the current program. The primary difference is
; that Exercise 5 does not require the programmer to store
; the input characters in a buffer.
.model small
.286
.stack 100h
.data
ScreenTitle db 'ACCOUNT INPUT SCREEN',0
AcctTitle db ' ACCT NUM:',0
NameTitle db ' LAST NAME:',0
PrevBalTitle db 'PREV BALANCE:',0
PymtsTitle db ' PAYMENTS:',0
CrTitle db ' CREDITS:',0
StartOfPrompts \
dw 0419h ; this section contains both
dw offset ScreenTitle ; screen positions for prompts
dw 060Fh ; and the addresses of the prompts
dw offset NameTitle ; The first entry is the row and
dw 080Fh ; column position for the first
dw offset AcctTitle ; prompt. The second entry is the
dw 0A0Fh ; address of the first prompt.
dw offset PrevBalTitle ; the display routine goes through
dw 0C0Fh ; this portion of the data, using
dw offset PymtsTitle ; these values to write to the
dw 0E0Fh ; screen
dw offset CrTitle
LastNameLen = 30
AcctNumLen = 6
PrevBalLen = 11
PaymentsLen = 11
CreditsLen = 11
NO_FILTER = -1
LastName \
dw LastNameLen
db 6,29 ; row, column position
db 70h ; attribute
db ? ; count actual chars typed
FILTER_OFFSET = ($ - LastName)
dw NO_FILTER
BUFFER_OFFSET = ($ - LastName) ; used by ShowInputField
db LastNameLen dup(0),0
AcctNum \
dw AcctNumLen
db 8,29 ; row, column position
db 70h ; attribute
118 IRVINE: SOLUTIONS MANUAL
PrevBal \
dw PrevBalLen
db 10,29 ; row, column position
db 70h ; attribute
db ? ; count actual chars typed
dw SignedNumberFilter
db PrevBalLen dup(0),0
Payments \
dw PaymentsLen
db 12,29 ; row, column position
db 70h ; attribute
db ? ; count actual chars typed
dw SignedNumberFilter
db PaymentsLen dup(0),0
Credits \
dw CreditsLen
db 14,29 ; row, column position
db 70h ; attribute
db ? ; count actual chars typed
dw SignedNumberFilter
db CreditsLen dup(0),0
LineLength dw 50
HorizLine db 196
VertLine db 179
TopLeft db 218
Botleft db 192
TopRight db 191
BotRight db 217
.Code
include library.inc
main proc
mov ax, @data ; init data segment
mov ds, ax
call SetupScreen ; Draw the input form
call WritePrompts ; write prompts to the screen
call GetFormData ; Get the user input
Exit:
mov ax, 4C00h ; end program
int 21h
main endp
GetFormData proc
CHAPTER 6 119
GF9:
ret
GetFormData endp
ENTER_KEY = 0Dh
TAB_KEY = 9
BKSP_KEY = 8
FILL_CHAR = '.'
ShowInputField proc
pusha
mov decimalCount,0
SIF2:
mov ah,8 ; get keystroke, no echo
int 21h ; AL = ASCII code
cmp al,0 ; extended key?
jne SIF2A ; no
int 21h ; yes - read extra kbyd byte
jmp SIF2 ; look for another key
SIF2A:
cmp [si+FILTER_OFFSET],NO_FILTER ; filter procedure specified?
jne SIF2B ; yes: skip next two lines
call EchoKey ; no: echo the character
jmp SIF3 ; and continue
SIF2B:
call near ptr [si+FILTER_OFFSET] ; call filter procedure
jnz SIF2 ; invalid character if ZF = 0
SIF3:
cmp al,ENTER_KEY ; look for exit keys
je SIF8 ; end of Form
cmp al,TAB_KEY
je SIF9 ; end of Field
cmp al,BKSP_KEY ; look for backspace
je SIF5
mov [di],al ; save the key
inc di
inc cx ; add to character count
cmp cx,[si] ; max characters reached?
jb SIF2
jmp SIF9 ; end of Field
SIF5:
cmp cx,0 ; first keystroke?
je SIF2 ; yes: get another keystroke
mov dx,offset EraseString ; erase the previous char
call Writestring
dec di ; back up in the buffer
dec cx ; decrement character count
jmp SIF2 ; get another keystroke
SIF8:
stc ; signal end of form
jmp SIF10
SIF9:
clc ; signal end of field
SIF10:
mov [si+6],cx ; save buffer count
popa
ret
.data
EraseString db BKSP_KEY, FILL_CHAR, BKSP_KEY,0
.code
CHAPTER 6 121
ShowInputField endp
SignedNumberFilter proc
pusha
cmp cx,0 ; first digit?
jne SNFA ; no
mov decimalCount,0 ; yes: init decimal count
SNFA:
mov di,offset ValidChars ; get filter string
call FindInString ; is AL in the string?
jnz SNF8 ; no, exit with beep
SNF3:
cmp al,ENTER_KEY ; don't echo the Enter key
je SNF9
cmp al,TAB_KEY ; don't echo the Tab key
je SNF9
cmp al,BKSP_KEY ; don't echo the Bksp key
je SNF9
call EchoKey
test al,0 ; set ZF = 1: Filter passed
jmp SNF9
SNF9:
popa
ret
122 IRVINE: SOLUTIONS MANUAL
.data
ValidChars db ".+-0123456789",TAB_KEY,BKSP_KEY,ENTER_KEY,0
decimalCount db ?
.code
SignedNumberFilter endp
FindInString proc
pusha
FIS1:
cmp [di],0 ; end of string?
je FIS2 ; if so, quit with ZF = 0
cmp [di],al ; check current character
je FIS3 ; if match found, exit with ZF = 1
inc di ; move to next character
jmp FIS1
FIS2:
or al,1 ; clear ZF = 0
FIS3:
popa
ret
FindInString endp
EchoKey proc
push ax
push dx
mov ah,2
mov dl,al
int 21h
pop dx
pop ax
ret
EchoKey endp
;------------------------------------------------------------------
SetupScreen proc
push dx
call Clrscr
mov dx, 050Ah ; cursor will be at row 5, col 10
call PlaceCursor
call DrawHorizontal ; draw the top line of the box
call DrawVertical ; and the left side
mov dx, 0F0Ah ; move cursor to row 15, col 10
call PlaceCursor
call DrawHorizontal ; Draw the bottom line
mov dx, 053Ch ; Place the cursor at row 5, col 60
call DrawVertical ; and draw the right side
CHAPTER 6 123
call DrawCorners
pop dx
ret
SetupScreen endp
PlaceCursor proc
mov ah, 02h ; BIOS function set cursor position
mov bh, 00h ; on video page 0
int 10h ; coordinates passed in dx
ret
PlaceCursor endp
DrawHorizontal proc
mov ah, 09h ; BIOS function write character
mov al, horizLine ; the horizontal line
mov bh, 00h ; on video page 0
mov bl, 07h ; with a normal attribute
mov cx, LineLength ; and size of the line
int 10h
ret
DrawHorizontal endp
DrawVertical proc
mov cx, 0Ah ; The box is 15 high
mov al, VertLine
mov bx, 0007 ; page 0 with a normal attribute
DrawIt:
push ax ; this routine draws a vertical line
push bx ; by incrementing the dh register,
push cx ; which contains the row at which the
mov cx, 1 ; character is to be written
int 10h ; the column in dl remains constant
inc dh ; The registers must be saved since
call PlaceCursor ; the same ones are used for drawing
pop cx ; and positioning the cursor
pop bx
pop ax
loop DrawIt
ret
DrawVertical endp
DrawOneChar proc
mov ah, 09h ; BIOS function write character
mov bx, 0007h ; video page 0 with normal attribute
mov cx, 1 ; only one character to write
int 10h
ret
DrawOneChar endp
DrawCorners proc
mov al, BotRight ; load the corner character
call DrawOneChar ; and draw it
mov dx, 050Ah ; move cursor to 5,10
call PlaceCursor
mov al, TopLeft ; Load the top left char
124 IRVINE: SOLUTIONS MANUAL
WritePrompts proc
mov cx, 6 ; number of prompts to write
mov bx, offset StartOfPrompts ; and the beginning of the data
WriteLoop:
mov dx, [bx] ; loads the cursor position into dx
call PlaceCursor ; and places it
add bx, 2 ; move bx to the next table entry
mov dx, [bx] ; which is the address of the prompt
call Writestring ; load it into dx, and write it out
add bx, 2 ; move bx to the next entry, which is
loop WriteLoop ; a cursor position, and repeat.
ret
WritePrompts endp
end main
.model small
.286
.stack 100
cr = 0dh ;Carriage return
lf = 0ah ;Line feed
.data
beforeMsg db "Array before sorting",cr,lf,0
afterMsg db cr,lf,"Array after Sorting",cr,lf,0
integer_array \
dw 16,178,41,-36,982,154,-3,76,453,-21,18,90,413,-233,142
dw 41,564,4,84,68,15,964,-38,815,150,901,713,852,384,520
dw 213,912,145,-630,412,241,970,125,302,745,145,-135,102,278
dw 145,640,-161,152,127,890
ARRAY_COUNT = ($ - integer_array) / (type integer_array)
.code
include library.inc
main proc
mov ax,@data ; Set up
CHAPTER 6 125
bubble_sort proc
pusha
dec cx ; decrement count by 1
Sort1:
push cx ; Save the outer loop count on the stack.
mov si,dx ; get pointer to first element
mov di,dx
add di,type integer_array ; point to second element
Sort2:
mov ax,[si] ; Get an array item
cmp [di],ax ; Compare the items
jge Sort3 ; if [si] <= [di] then no swap needed
xchg ax,[di] ; Swap the two values
mov [si],ax
Sort3:
add si,type integer_array
add di,type integer_array
loop Sort2 ; inner loop
popa
ret
bubble_sort endp
print_array proc
pusha
mov si,offset integer_array
mov cx,ARRAY_COUNT
126 IRVINE: SOLUTIONS MANUAL
PA1:
mov ax,[si] ; get a value
call Writeint_signed ; display it
push dx
mov dx,offset tabStr
call Writestring
pop dx
inc dx ; column counter
cmp dx,4 ; last column reached?
jb PA2
call Crlf ; yes: write EOLN
mov dx,0
PA2:
add si,2
loop PA1
call Crlf
popa
ret
.data
tabStr db 9,0
.code
print_array endp
end main
.model small
.286
.stack 100h
.data
array \
dw 16,178,41,-36,982,154,-3,76,453,-21,18,90,413,-233,142
dw 41,564,4,84,68,15,964,-38,815,150,901,713,852,384,520
dw 213,912,145,-630,412,241,970,125,302,745,145,-135,102,278
dw 145,640,-161,152,127,890
ARRAY_COUNT = ($ - array) / (type array)
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
CHAPTER 6 127
L2:
mov dx,offset notFoundMsg ; "Not found"
call Writestring
L3:
call Crlf
mov ax,4c00h ; end program
int 21h
main endp
BubbleSort proc
pusha
dec cx ; decrement count by 1
Sort1:
push cx ; Save the outer loop count on the stack:
mov si,dx ; get pointer to first element
mov di,dx
add di,2 ; point to second element
Sort2:
mov ax,[si] ; Get an array item
cmp [di],ax ; Compare the items
jge Sort3 ; if [si] <= [di] then no swap needed
xchg ax,[di] ; Swap the two values
mov [si],ax
Sort3:
128 IRVINE: SOLUTIONS MANUAL
popa
ret
BubbleSort endp
; Search the array for the value in AX. The array length
; is in CX. If a match is found, return
; its array position in AX. If no match is found, set AX to -1.
BinarySearch proc
push bx
push si
mov ax,last
add ax,first
shr ax,1
mov mid,ax
mov si,mid
shl si,1
cmp array[si],bx
jge BS2
mov ax,mid
inc ax
mov first,ax
jmp BS4
BS2:
mov si,mid
CHAPTER 6 129
shl si,1
cmp array[si],bx
jle BS3
mov ax,mid
dec ax
mov last,ax
jmp BS4
BS3:
mov ax,mid ; value found: return its position
jmp BS9
; endif
BS4:
jmp BS1 ; continue the loop
mov ax,-1
jmp BS9
BS9:
pop si
pop bx
ret
.data
first dw ?
last dw ?
mid dw ?
.code
BinarySearch endp
PrintArray proc
pusha
mov si,offset array
mov cx,ARRAY_COUNT
mov dx,0 ; column counter
PA1:
mov ax,[si] ; get a value
call Writeint_signed ; display it
push dx
mov dx,offset tabStr
call Writestring
pop dx
inc dx ; column counter
cmp dx,4 ; last column reached?
jb PA2
call Crlf ; yes: write EOLN
mov dx,0
130 IRVINE: SOLUTIONS MANUAL
PA2:
add si,2
loop PA1
call Crlf
popa
ret
.data
tabStr db 9,0
.code
PrintArray endp
end main
; Notes:
; The primary difficulty in getting this program to work lies
; in the filtering of control characters by DOS. The encryption
; operation frequently creates output files containing 1Ah (EOF)
; characters, for example. If you try to read such a file with
; INT 21h functions 6,7,or 8, they stop at the 1Ah character.
; The solution used here is to read the file with INT 21h
; function 3Fh (Read from File or Device), which is explained
; on page 153 in Chapter 5. Writing to the file is easiest
; with INT 21h function 40h (Write to File or Device), which is
; explained on page 452 (Chapter 12). Therefore, you can ask
; students to read just that one page.
.model small
.286
.stack 100h
.data
key db "alkwiourwsja.zx,vmc098230.jfsd0s8sfnm,nn,n34,5n3,$"
db "8 83,fpd;n8mnbv'a90xbib;gmgfi lf,duutb8f9fkemsmdm#"
db "8sm,dsmuwmnwpocnwy^%8@#$%^&x"
KEY_SIZE = ($ - key)
BUF_SIZE = KEY_SIZE
bytesRead dw 0
buffer db BUF_SIZE dup(0)
handle dw 0
.code
include library.inc
main proc
mov ax,@data ; init data segment
mov ds,ax
call ClrScr
CHAPTER 6 131
call Encrypt
Encrypt proc
; Read from the file into the input buffer and
; encrypt all characters in the buffer with their
; matching values in the encryption key.
E1:
call ReadBuf
mov si,0
mov cx,bytesRead
cmp cx,0
je E9
mov ah,40h
mov bx,1
mov cx,bytesRead
mov dx,offset buffer
int 21h
cmp bytesRead,BUF_SIZE
je E1
E9:
ret
Encrypt endp
ReadBuf proc
pusha
popa
ret
ReadBuf endp
end main
132 IRVINE: SOLUTIONS MANUAL