8086汇编语言初学者教程(第5部分)
常用函数库 - emu8086.inc
通过引用一些常用函数,可以使你编程更加方便。
在你的程序中使用其他文件中的函数的方法是
INCLUDE后面接上你要引用的文件名。编译器
会自动在你源程序所在的文件夹中查找你引用
的文件,如果没有找到,它将搜索Inc 文件夹。
通常你无法完全理解 emu8086.inc(位于Inc文件夹)
但是这没有关系,你只用知道它能做什么就足够了。
要使用emu8086.inc中的函数,你应当在你程序的开
头加上
include 'emu8086.inc'
emu8086.inc 定义了如下的宏:
使用上述宏的方法是:在你需要的位值写上宏名称加上参数。例如:
include emu8086.inc
ORG 100h
PRINT 'Hello World!'
GOTOXY 10, 5
PUTC 65 ; 65 - ASCII 码的 'A'
PUTC 'B'
RET ; 返回操作系统
END ; 停止编译器 |
当编译器运行你的代码时,它首先找到声明中的
emu8086.inc文件,然后将代码中的宏用实际的
代码替换掉。通常来说,宏都是比较小的代码段,
经常使用宏会使得你的可执行程序特别大
(对于降低文件大小来说使用过程更好)
emu8086.inc 同样定义了如下过程:
使用上述过程,必须在你源程序的底部(但是在END之前!!!)声明这些函数,使用CALL指令后面接上过程名称来调用。例如:
include 'emu8086.inc'
ORG 100h
LEA SI, msg1 ; 要求输入数字
CALL print_string ;
CALL scan_num ; 读取数字放入cx
MOV AX, CX ; CX存放数值拷贝到AX
; 输入如下字符
CALL pthis
DB 13, 10, 'You have entered: ', 0
CALL print_num ; 输出 AX中的字符
RET ; 返回操作系统
msg1 DB 'Enter the number: ', 0
DEFINE_SCAN_NUM
DEFINE_PRINT_STRING
DEFINE_PRINT_NUM
DEFINE_PRINT_NUM_UNS ; print_num函数要求的
DEFINE_PTHIS
END ; 结束
|
首先,编译器运行声明(对于宏只是展开)。当编译器遇到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汇编基础】06--算术运算与逻辑指令