8086汇编语言初学者教程(第5部分)
常用函数库 - emu8086.inc 通过引用一些常用函数,可以使你编程更加方便。在你的程序中使用其他文件中的函数的方法是INCLUDE后面接上你要引用的文件名。编译器会自动在你源程序所在的文件夹中查找你引用的文件,如果没有找到,它将搜索Inc 文件夹。通常你无法完全理解 emu8086.inc(位于Inc文件夹)但是这没有关系,你只用知道它能做什么就足够了。要使用emu8086.inc中的函数,你应当在你程序的开头加上include 'emu8086.inc'
emu8086.inc 定义了如下的宏:
- PUTC char - 将一个ascii字符输出到光标当前位值,只有一个 参数的宏
- GOTOXY col, row - 设置当前光标位置,有两个参数
- PRINT string - 输出字符串,一个参数
- PRINTN string - 输出字符串,一个参数。与print功能相同, 不同在于输出之后自动回车
- CURSOROFF - 关闭文本光标
- CURSORON - 打开文本光标
使用上述宏的方法是:在你需要的位值写上宏名称加上参数。例如:
|
当编译器运行你的代码时,它首先找到声明中的emu8086.inc文件,然后将代码中的宏用实际的代码替换掉。通常来说,宏都是比较小的代码段,经常使用宏会使得你的可执行程序特别大(对于降低文件大小来说使用过程更好)
emu8086.inc 同样定义了如下过程:
- PRINT_STRING - 在当前光标位置输出一个字符串字符串地址由DS:SI 寄存器给出使用时,需要在END前面声明DEFINE_PRINT_STRING 才能使用.
- PTHIS - 在当前光标位置输出一个字符串(同 PRINT_STRING)
一样,不同之处在于它是从堆栈接收字符串。字符串终止符
应在call之后定义。例如 CALL PTHIS db 'Hello World!', 0
使用时,需要在 END 前面声明 DEFINE_PTHIS 。 - GET_STRING - 从用户输入得到一个字符串,输入的字符串写入 DS:DI 指出的缓冲,缓冲区的大小由 DX设置。回车作为输入结束。使用时,需要在END前面声明 DEFINE_GET_STRING 。
- CLEAR_SCREEN - 清屏过程(滚过整个屏幕),然后将光标设置在左上角. 使用时,需要在END前面声明DEFINE_CLEAR_SCREEN 。
- SCAN_NUM - 取得用户从键盘输入的多位有符号数,并将输入存放
在CX寄存器。 使用时,需要在 END前面声明 DEFINE_SCAN_NUM。 - PRINT_NUM - 输出AX寄存器中的有符号数
使用时,需要在END 前面声明 DEFINE_PRINT_NUM以及 DEFINE_PRINT_NUM_UNS. - PRINT_NUM_UNS - 输出AX寄存器中的无符号数。使用时,需要在END 前面声明DEFINE_PRINT_NUM_UNS.
使用上述过程,必须在你源程序的底部(但是在END之前!!!)声明这些函数,使用CALL指令后面接上过程名称来调用。例如:
|
首先,编译器运行声明(对于宏只是展开)。当编译器遇到CALL指令,它
将用过程声明中的地址来替代过程名。程序在执行过程中遇到这个过程,便
会直接跳转到过程。这是非常有用的,比如,即使在你的代码中执行100次
一个过程,编译后的可执行文件也不会因此而增大多少。这样看起来很
划算,是不是?后面你会学到更多的,现在只需要了解一点点基本原理。
emu8086.inc文件内容如下:
1 ; emu8086.inc - macro definitions library for easy input/output
2
3
4
5
6 ; Note, that some declarations of "emu8086.inc" are macro procedure declarations, and you
7 ; have to use "DEFINE_..." macro somewhere in your program if you want to use these functions:
8
9 ; CALL SCAN_NUM
10 ; CALL PRINT_STRING
11 ; CALL PTHIS
12 ; CALL GET_STRING
13 ; CALL CLEAR_SCREEN
14 ; CALL PRINT_NUM
15 ; CALL PRINT_NUM_UNS
16
17 ; You can define all these procedures in your source code, but compilation time may slow down
18 ; sufficiently because of that, only declare functions that you plan to use:
19
20
21 ; DEFINE_SCAN_NUM
22 ; DEFINE_PRINT_STRING
23 ; DEFINE_PTHIS
24 ; DEFINE_GET_STRING
25 ; DEFINE_CLEAR_SCREEN
26 ; DEFINE_PRINT_NUM
27 ; DEFINE_PRINT_NUM_UNS
28
29 ; The above declarations should be made in your code once only! Better somewhere
30 ; in the end of your file, but before "END" directive. You can also declare them
31 ; in the beginning of the file, but it should be after "ORG 100h" directive for COM files,
32 ; or inside the code segment for EXE files.
33
34
35
36
37
38
39
40
41
42 ; this macro prints a char in AL and advances
43 ; the current cursor position:
44 PUTC MACRO char
45 PUSH AX
46 MOV AL, char
47 MOV AH, 0Eh
48 INT 10h
49 POP AX
50 ENDM
51
52
53 ; this macro prints a string that is given as a parameter, example:
54 ; PRINT 'hello world!'
55 ; new line is NOT added.
56 PRINT MACRO sdat
57 LOCAL next_char, s_dcl, printed, skip_dcl
58
59 PUSH AX ; store registers...
60 PUSH SI ;
61
62 JMP skip_dcl ; skip declaration.
63 s_dcl DB sdat, 0
64
65 skip_dcl:
66 LEA SI, s_dcl
67
68 next_char:
69 MOV AL, CS:[SI]
70 CMP AL, 0
71 JZ printed
72 INC SI
73 MOV AH, 0Eh ; teletype function.
74 INT 10h
75 JMP next_char
76 printed:
77
78 POP SI ; re-store registers...
79 POP AX ;
80 ENDM
81
82
83 ; this macro prints a string that is given as a parameter, example:
84 ; PRINTN 'hello world!'
85 ; the same as PRINT, but new line is automatically added.
86 PRINTN MACRO sdat
87 LOCAL next_char, s_dcl, printed, skip_dcl
88
89 PUSH AX ; store registers...
90 PUSH SI ;
91
92 JMP skip_dcl ; skip declaration.
93 s_dcl DB sdat, 13, 10, 0
94
95 skip_dcl:
96 LEA SI, s_dcl
97
98 next_char:
99 MOV AL, CS:[SI]
100 CMP AL, 0
101 JZ printed
102 INC SI
103 MOV AH, 0Eh ; teletype function.
104 INT 10h
105 JMP next_char
106 printed:
107
108 POP SI ; re-store registers...
109 POP AX ;
110 ENDM
111
112
113 ; turns off the cursor:
114 CURSOROFF MACRO
115 PUSH AX
116 PUSH CX
117 MOV AH, 1
118 MOV CH, 28h
119 MOV CL, 09h
120 INT 10h
121 POP CX
122 POP AX
123 ENDM
124
125
126
127 ; turns on the cursor:
128 CURSORON MACRO
129 PUSH AX
130 PUSH CX
131 MOV AH, 1
132 MOV CH, 08h
133 MOV CL, 09h
134 INT 10h
135 POP CX
136 POP AX
137 ENDM
138
139 ; sets current cursor
140 ; position:
141 GOTOXY MACRO col, row
142 PUSH AX
143 PUSH BX
144 PUSH DX
145 MOV AH, 02h
146 MOV DH, row
147 MOV DL, col
148 MOV BH, 0
149 INT 10h
150 POP DX
151 POP BX
152 POP AX
153 ENDM
154
155
156 ;***************************************************************
157
158 ; This macro defines a procedure that gets the multi-digit SIGNED number from the keyboard,
159 ; and stores the result in CX register:
160 DEFINE_SCAN_NUM MACRO
161 LOCAL make_minus, ten, next_digit, set_minus
162 LOCAL too_big, backspace_checked, too_big2
163 LOCAL stop_input, not_minus, skip_proc_scan_num
164 LOCAL remove_not_digit, ok_AE_0, ok_digit, not_cr
165
166 ; protect from wrong definition location:
167 JMP skip_proc_scan_num
168
169 SCAN_NUM PROC NEAR
170 PUSH DX
171 PUSH AX
172 PUSH SI
173
174 MOV CX, 0
175
176 ; reset flag:
177 MOV CS:make_minus, 0
178
179 next_digit:
180
181 ; get char from keyboard
182 ; into AL:
183 MOV AH, 00h
184 INT 16h
185 ; and print it:
186 MOV AH, 0Eh
187 INT 10h
188
189 ; check for MINUS:
190 CMP AL, '-'
191 JE set_minus
192
193 ; check for ENTER key:
194 CMP AL, 13 ; carriage return?
195 JNE not_cr
196 JMP stop_input
197 not_cr:
198
199
200 CMP AL, 8 ; 'BACKSPACE' pressed?
201 JNE backspace_checked
202 MOV DX, 0 ; remove last digit by
203 MOV AX, CX ; division:
204 DIV CS:ten ; AX = DX:AX / 10 (DX-rem).
205 MOV CX, AX
206 PUTC ' ' ; clear position.
207 PUTC 8 ; backspace again.
208 JMP next_digit
209 backspace_checked:
210
211
212 ; allow only digits:
213 CMP AL, '0'
214 JAE ok_AE_0
215 JMP remove_not_digit
216 ok_AE_0:
217 CMP AL, '9'
218 JBE ok_digit
219 remove_not_digit:
220 PUTC 8 ; backspace.
221 PUTC ' ' ; clear last entered not digit.
222 PUTC 8 ; backspace again.
223 JMP next_digit ; wait for next input.
224 ok_digit:
225
226
227 ; multiply CX by 10 (first time the result is zero)
228 PUSH AX
229 MOV AX, CX
230 MUL CS:ten ; DX:AX = AX*10
231 MOV CX, AX
232 POP AX
233
234 ; check if the number is too big
235 ; (result should be 16 bits)
236 CMP DX, 0
237 JNE too_big
238
239 ; convert from ASCII code:
240 SUB AL, 30h
241
242 ; add AL to CX:
243 MOV AH, 0
244 MOV DX, CX ; backup, in case the result will be too big.
245 ADD CX, AX
246 JC too_big2 ; jump if the number is too big.
247
248 JMP next_digit
249
250 set_minus:
251 MOV CS:make_minus, 1
252 JMP next_digit
253
254 too_big2:
255 MOV CX, DX ; restore the backuped value before add.
256 MOV DX, 0 ; DX was zero before backup!
257 too_big:
258 MOV AX, CX
259 DIV CS:ten ; reverse last DX:AX = AX*10, make AX = DX:AX / 10
260 MOV CX, AX
261 PUTC 8 ; backspace.
262 PUTC ' ' ; clear last entered digit.
263 PUTC 8 ; backspace again.
264 JMP next_digit ; wait for Enter/Backspace.
265
266
267 stop_input:
268 ; check flag:
269 CMP CS:make_minus, 0
270 JE not_minus
271 NEG CX
272 not_minus:
273
274 POP SI
275 POP AX
276 POP DX
277 RET
278 make_minus DB ? ; used as a flag.
279 ten DW 10 ; used as multiplier.
280 SCAN_NUM ENDP
281
282 skip_proc_scan_num:
283
284 DEFINE_SCAN_NUM ENDM
285 ;***************************************************************
286
287
288 ;***************************************************************
289 ; this macro defines a procedure to print a null terminated
290 ; string at current cursor position, receives address of string in DS:SI
291 DEFINE_PRINT_STRING MACRO
292 LOCAL next_char, printed, skip_proc_print_string
293
294 ; protect from wrong definition location:
295 JMP skip_proc_print_string
296
297 PRINT_STRING PROC NEAR
298 PUSH AX ; store registers...
299 PUSH SI ;
300
301 next_char:
302 MOV AL, [SI]
303 CMP AL, 0
304 JZ printed
305 INC SI
306 MOV AH, 0Eh ; teletype function.
307 INT 10h
308 JMP next_char
309 printed:
310
311 POP SI ; re-store registers...
312 POP AX ;
313
314 RET
315 PRINT_STRING ENDP
316
317 skip_proc_print_string:
318
319 DEFINE_PRINT_STRING ENDM
320 ;***************************************************************
321
322
323 ;***************************************************************
324 ; This macro defines a procedure to print a null terminated
325 ; string at current cursor position.
326 ; The ZERO TERMINATED string should be defined just after the CALL. For example:
327 ;
328 ; CALL PTHIS
329 ; db 'Hello World!', 0
330 ;
331 ; Address of string is stored in the Stack as return address.
332 ; Procedure updates value in the Stack to make return
333 ; after string definition.
334 DEFINE_PTHIS MACRO
335 LOCAL next_char, printed, skip_proc_pthis, temp1
336
337 ; protect from wrong definition location:
338 JMP skip_proc_pthis
339
340 PTHIS PROC NEAR
341
342 MOV CS:temp1, SI ; store SI register.
343
344 POP SI ; get return address (IP).
345
346 PUSH AX ; store AX register.
347
348 next_char:
349 MOV AL, CS:[SI]
350 INC SI ; next byte.
351 CMP AL, 0
352 JZ printed
353 MOV AH, 0Eh ; teletype function.
354 INT 10h
355 JMP next_char ; loop.
356 printed:
357
358 POP AX ; re-store AX register.
359
360 ; SI should point to next command after
361 ; the CALL instruction and string definition:
362 PUSH SI ; save new return address into the Stack.
363
364 MOV SI, CS:temp1 ; re-store SI register.
365
366 RET
367 temp1 DW ? ; variable to store original value of SI register.
368 PTHIS ENDP
369
370 skip_proc_pthis:
371
372 DEFINE_PTHIS ENDM
373 ;***************************************************************
374
375
376 ;***************************************************************
377 ; This macro defines a procedure to get a null terminated
378 ; string from user, the received string is written to buffer
379 ; at DS:DI, buffer size should be in DX.
380 ; Procedure stops the input when 'Enter' is pressed.
381 DEFINE_GET_STRING MACRO
382 LOCAL empty_buffer, wait_for_key, skip_proc_get_string
383 LOCAL exit, add_to_buffer
384
385 ; protect from wrong definition location:
386 JMP skip_proc_get_string
387
388 GET_STRING PROC NEAR
389 PUSH AX
390 PUSH CX
391 PUSH DI
392 PUSH DX
393
394 MOV CX, 0 ; char counter.
395
396 CMP DX, 1 ; buffer too small?
397 JBE empty_buffer ;
398
399 DEC DX ; reserve space for last zero.
400
401
402 ;============================
403 ; loop to get and processes key presses:
404
405 wait_for_key:
406
407 MOV AH, 0 ; get pressed key.
408 INT 16h
409
410 CMP AL, 13 ; 'RETURN' pressed?
411 JZ exit
412
413
414 CMP AL, 8 ; 'BACKSPACE' pressed?
415 JNE add_to_buffer
416 JCXZ wait_for_key ; nothing to remove!
417 DEC CX
418 DEC DI
419 PUTC 8 ; backspace.
420 PUTC ' ' ; clear position.
421 PUTC 8 ; backspace again.
422 JMP wait_for_key
423
424 add_to_buffer:
425
426 CMP CX, DX ; buffer is full?
427 JAE wait_for_key ; if so wait for 'BACKSPACE' or 'RETURN'...
428
429 MOV [DI], AL
430 INC DI
431 INC CX
432
433 ; print the key:
434 MOV AH, 0Eh
435 INT 10h
436
437 JMP wait_for_key
438 ;============================
439
440 exit:
441
442 ; terminate by null:
443 MOV [DI], 0
444
445 empty_buffer:
446
447 POP DX
448 POP DI
449 POP CX
450 POP AX
451 RET
452 GET_STRING ENDP
453
454
455 skip_proc_get_string:
456
457 DEFINE_GET_STRING ENDM
458 ;***************************************************************
459
460 ;***************************************************************
461 ; this macro defines procedure to clear the screen,
462 ; (done by scrolling entire screen window),
463 ; and set cursor position to top of it:
464 DEFINE_CLEAR_SCREEN MACRO
465 LOCAL skip_proc_clear_screen
466
467 ; protect from wrong definition location:
468 JMP skip_proc_clear_screen
469
470 CLEAR_SCREEN PROC NEAR
471 PUSH AX ; store registers...
472 PUSH DS ;
473 PUSH BX ;
474 PUSH CX ;
475 PUSH DI ;
476
477 MOV AX, 40h
478 MOV DS, AX ; for getting screen parameters.
479 MOV AH, 06h ; scroll up function id.
480 MOV AL, 0 ; scroll all lines!
481 MOV BH, 07 ; attribute for new lines.
482 MOV CH, 0 ; upper row.
483 MOV CL, 0 ; upper col.
484 MOV DI, 84h ; rows on screen -1,
485 MOV DH, [DI] ; lower row (byte).
486 MOV DI, 4Ah ; columns on screen,
487 MOV DL, [DI]
488 DEC DL ; lower col.
489 INT 10h
490
491 ; set cursor position to top
492 ; of the screen:
493 MOV BH, 0 ; current page.
494 MOV DL, 0 ; col.
495 MOV DH, 0 ; row.
496 MOV AH, 02
497 INT 10h
498
499 POP DI ; re-store registers...
500 POP CX ;
501 POP BX ;
502 POP DS ;
503 POP AX ;
504
505 RET
506 CLEAR_SCREEN ENDP
507
508 skip_proc_clear_screen:
509
510 DEFINE_CLEAR_SCREEN ENDM
511 ;***************************************************************
512
513
514 ;***************************************************************
515
516 ; This macro defines a procedure that prints number in AX,
517 ; used with PRINT_NUM_UNS to print signed numbers:
518 ; Requires DEFINE_PRINT_NUM_UNS !!!
519 DEFINE_PRINT_NUM MACRO
520 LOCAL not_zero, positive, printed, skip_proc_print_num
521
522 ; protect from wrong definition location:
523 JMP skip_proc_print_num
524
525 PRINT_NUM PROC NEAR
526 PUSH DX
527 PUSH AX
528
529 CMP AX, 0
530 JNZ not_zero
531
532 PUTC '0'
533 JMP printed
534
535 not_zero:
536 ; the check SIGN of AX,
537 ; make absolute if it's negative:
538 CMP AX, 0
539 JNS positive
540 NEG AX
541
542 PUTC '-'
543
544 positive:
545 CALL PRINT_NUM_UNS
546 printed:
547 POP AX
548 POP DX
549 RET
550 PRINT_NUM ENDP
551
552 skip_proc_print_num:
553
554 DEFINE_PRINT_NUM ENDM
555
556 ;***************************************************************
557
558 ; This macro defines a procedure that prints out an unsigned
559 ; number in AX (not just a single digit)
560 ; allowed values from 0 to 65535 (0FFFFh)
561 DEFINE_PRINT_NUM_UNS MACRO
562 LOCAL begin_print, calc, skip, print_zero, end_print, ten
563 LOCAL skip_proc_print_num_uns
564
565 ; protect from wrong definition location:
566 JMP skip_proc_print_num_uns
567
568 PRINT_NUM_UNS PROC NEAR
569 PUSH AX
570 PUSH BX
571 PUSH CX
572 PUSH DX
573
574 ; flag to prevent printing zeros before number:
575 MOV CX, 1
576
577 ; (result of "/ 10000" is always less or equal to 9).
578 MOV BX, 10000 ; 2710h - divider.
579
580 ; AX is zero?
581 CMP AX, 0
582 JZ print_zero
583
584 begin_print:
585
586 ; check divider (if zero go to end_print):
587 CMP BX,0
588 JZ end_print
589
590 ; avoid printing zeros before number:
591 CMP CX, 0
592 JE calc
593 ; if AX<BX then result of DIV will be zero:
594 CMP AX, BX
595 JB skip
596 calc:
597 MOV CX, 0 ; set flag.
598
599 MOV DX, 0
600 DIV BX ; AX = DX:AX / BX (DX=remainder).
601
602 ; print last digit
603 ; AH is always ZERO, so it's ignored
604 ADD AL, 30h ; convert to ASCII code.
605 PUTC AL
606
607
608 MOV AX, DX ; get remainder from last div.
609
610 skip:
611 ; calculate BX=BX/10
612 PUSH AX
613 MOV DX, 0
614 MOV AX, BX
615 DIV CS:ten ; AX = DX:AX / 10 (DX=remainder).
616 MOV BX, AX
617 POP AX
618
619 JMP begin_print
620
621 print_zero:
622 PUTC '0'
623
624 end_print:
625
626 POP DX
627 POP CX
628 POP BX
629 POP AX
630 RET
631 ten DW 10 ; used as divider.
632 PRINT_NUM_UNS ENDP
633
634 skip_proc_print_num_uns:
635
636 DEFINE_PRINT_NUM_UNS ENDM
637 ;***************************************************************
638
639
640
本文为8086汇编语言初学者提供了一个详细的教程,介绍了如何使用宏和函数库,特别是emu8086.inc文件中的常用函数,以简化编程工作。通过包含特定的头文件并使用预定义的宏和过程,程序员可以更高效地进行输入/输出操作、字符串处理、数字输入与输出、屏幕清理等任务。
2382

被折叠的 条评论
为什么被折叠?



