【8086汇编基础】05--常用函数库文件--emu8086.inc

本文为8086汇编语言初学者提供了一个详细的教程,介绍了如何使用宏和函数库,特别是emu8086.inc文件中的常用函数,以简化编程工作。通过包含特定的头文件并使用预定义的宏和过程,程序员可以更高效地进行输入/输出操作、字符串处理、数字输入与输出、屏幕清理等任务。

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 - 打开文本光标

使用上述宏的方法是:在你需要的位值写上宏名称加上参数。例如:


include emu8086.inc

ORG 100h

PRINT 'Hello World!'

GOTOXY 10, 5

PUTC 65 ; 65 - ASCII 码的 'A'
PUTC 'B'

RET ; 返回操作系统
END ; 停止编译器

当编译器运行你的代码时,它首先找到声明中的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指令后面接上过程名称来调用。例如:

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文件内容如下:

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汇编基础】04--中断

>>> 下一部分 >>>【8086汇编基础】06--算术运算与逻辑指令

<think>我们参考用户提供的引用内容,特别是引用[1]和引用[2]中提到了EMU8086和Proteus。 用户需求:查找使用EMU8086和Proteus实现hello world小程序的方法。 分析: 1. EMU8086是一个8086汇编语言模拟器,用于编写、汇编和调试8086程序。 2. Proteus是一个电子设计自动化软件,用于模拟微控制器和外围电路,支持硬件仿真。 3. 实现“hello world”程序,通常有两种方式: - 纯软件方式:在EMU8086中编写汇编程序,运行后在模拟器的输出窗口显示字符串- 硬件仿真方式:在Proteus中设计硬件电路(包括8086处理器和外围设备如显示器),然后编写程序并在Proteus中仿真。 由于用户同时提到了EMU8086和Proteus,我们需要分别说明两种环境下的实现方法,并可能涉及如何将两者结合(例如在EMU8086中编写程序,然后将生成的二进制文件加载到Proteus的8086微处理器中运行)。 步骤: 一、在EMU8086中实现Hello World(软件方式) 8086汇编程序可以通过系统调用来输出字符串。在DOS环境下,我们可以使用INT 21h中断的功能09h来显示字符串。 示例程序: ``` org 100h ; 程序起始地址(COM格式) mov dx, msg ; DX指向字符串地址 mov ah, 9 ; 功能号9(显示字符串) int 21h ; 调用DOS中断 mov ah, 4Ch ; 功能号4Ch(程序结束) int 21h ; 调用DOS中断 msg db 'Hello, World!', '$' ; 以$结尾的字符串 ``` 在EMU8086中,我们可以直接编写这段代码,然后运行,模拟器会显示输出。 二、在Proteus中实现Hello World(硬件仿真方式) 在Proteus中,我们需要构建一个包含8086处理器和输出设备(如LED、LCD或虚拟终端)的电路。这里我们可以使用虚拟终端(Virtual Terminal)来显示字符。 步骤: 1. 绘制电路图: - 添加8086 CPU(在Proteus的微处理器库中选择8086- 添加必要的时钟电路和复位电路 - 添加虚拟终端(在虚拟仪器模式中选择VIRTUAL TERMINAL) - 将虚拟终端的RXD连接到8086的TXD(串行通信,这里我们使用串行口,因此还需要配置8284时钟发生器和8251/8250串行控制器,或者使用8086的串行端口,但8086本身不直接提供串行端口,需要外接芯片。为了简化,我们可以使用8250 UART芯片,或者利用Proteus的虚拟终端直接连接8086的串行引脚?实际上,8086需要通过并行转串行的芯片(如8251)来实现串行通信,但Proteus的8086模型支持直接连接虚拟终端吗?) 实际上,在Proteus中,我们可以简化步骤,使用8086的并行端口直接驱动LED显示,但显示字符串通常使用串行终端或并口连接字符显示器。更简单的方法是利用Proteus的虚拟终端,通过串行通信输出。 我们需要使用8250 UART(或类似的串行控制器)来连接虚拟终端。电路连接如下: - 8086的数据总线和控制信号连接到8250芯片 - 8250的串行输出(TX)连接到虚拟终端的RXD 2. 编写汇编程序: 程序需要初始化8250(设置波特率、数据位、停止位等),然后通过8250发送字符串“Hello World”。 3.汇编程序编译成二进制文件.bin或.hex),然后加载到8086处理器的程序存储器中。 但是,这个过程较为复杂。另一种简化方法是使用Proteus中8086的预定义串行端口(如果支持的话)。实际上,Proteus中的8086模型支持直接连接串行终端,我们可以使用INT 21H功能(DOS中断)吗?注意,Proteus中运行8086程序通常需要BIOS和DOS的支持,但Proteus并没有内置完整的DOS系统。因此,我们需要直接控制硬件。 考虑到复杂性,我们可以选择使用并行端口输出到LCD,但这样代码量较大。或者,我们可以使用Proteus的调试功能,在调试窗口输出信息?但这不是“显示”在电路上。 这里我们提供一个使用串行端口输出到虚拟终端的简单示例(不使用DOS中断,而是直接控制8250芯片): 硬件连接:8086连接8250,8250连接虚拟终端。 程序步骤: - 初始化8250(设置波特率等) - 循环发送字符串中的每个字符 由于具体电路设计和编程细节较为复杂,且用户要求Hello World小程序,我们可能更倾向于在EMU8086中实现软件方式,而在Proteus中实现硬件仿真可以采用更简单的方式:比如使用8086控制一个七段数码管或LED显示一些数字,但显示字符串通常需要LCD或串行终端。为了节省时间,我们重点介绍EMU8086中的实现,然后简要说明Proteus中如何通过串行终端显示。 三、结合使用EMU8086和Proteus 1.EMU8086中编写并调试汇编程序(使用INT 21H显示字符串)。 2. 将程序编译成二进制文件.com或.exe,但Proteus需要加载二进制镜像)。 3. 在Proteus中设计8086最小系统,并连接虚拟终端(通过串行口)。 注意:我们需要在Proteus中设置8086的时钟频率,并连接好复位电路和必要的存储器(ROM和RAM)。 4.EMU8086生成的二进制文件加载到Proteus中8086系统的ROM中(需要正确设置起始地址)。 5. 运行仿真,观察虚拟终端输出。 但是,由于DOS中断依赖于DOS系统,而Proteus中没有DOS,所以如果我们编写的是DOS程序(使用INT 21h),则无法在Proteus中直接运行。因此,在Proteus中我们需要编写直接控制硬件的程序(如直接写显存或控制串行芯片)。 考虑到用户的需求是“实现hello world小程序”,我们重点给出两种独立的方法: 方法一:EMU8086中简单的Hello World程序(如上所述) 方法二:Proteus中通过串行终端输出Hello World(硬件仿真) 由于在Proteus中直接控制串行芯片的汇编程序较长,且需要硬件知识,我们提供一个简化版本: 在Proteus中,我们可以使用8086模型自带的串行端口(如果支持)?实际上,在Proteus中,8086模型有一个内置的串行端口,我们可以直接使用INT 14H(BIOS串行通信中断)来输出字符?或者,我们可以直接操作串行端口的I/O地址。 经过查阅,在Proteus中仿真8086时,我们可以使用虚拟终端直接连接到8086的串行引脚(但8086本身没有串行引脚,需要外接8251或8250)。因此,我们使用8250 UART。 步骤: 1. 在Proteus中放置8086、8250、VIRTUAL TERMINAL。 2. 连接: - 8086的数据总线(D0-D7)连接到8250的数据线(D0-D7) - 8086的地址总线(A0-A2)连接到8250的地址线(A0-A2) - 8086的IOR和IOW连接到8250的RD和WR - 8086的M/IO连接到低电平(表示I/O操作) - 8250的片选CS由地址译码器产生(为了简单,我们可以将8250的CS0接高,CS1和CS2接低,然后用一个简单的逻辑,比如当A3-A15为0时选中,这样8250的端口地址为0x00-0x07) - 8250的TX连接到虚拟终端的RXD - 虚拟终端的波特率设置为9600,数据位8,无校验,停止位1(与程序初始化一致) 3. 编写汇编程序: 初始化8250: MOV AL, 80h ; 设置DLAB=1(访问波特率除数) OUT 03h, AL ; 写入线路控制寄存器(端口3) MOV AL, 0Ch ; 波特率除数低位(9600波特率,假设时钟1.8432MHz,除数=1843200/(9600*16)=12) OUT 00h, AL MOV AL, 00h ; 波特率除数高位 OUT 01h, AL MOV AL, 03h ; 设置数据格式:8位数据,1位停止,无校验,DLAB=0 OUT 03h, AL 发送字符函数(发送AL中的字符): PUSH AX WAIT_TX: IN AL, 05h ; 读线路状态寄存器(端口5) AND AL, 20h ; 检查发送保持寄存器空(THRE)位 JZ WAIT_TX POP AX OUT 00h, AL ; 发送字符(端口0) 主程序: LEA SI, msg NEXT_CHAR: MOV AL, [SI] CMP AL, 0 JZ DONE CALL SEND_CHAR INC SI JMP NEXT_CHAR DONE: HLT 数据段: msg db 'Hello, World!',0 4. 将程序编译成二进制文件(可以使用EMU8086,但注意EMU8086默认生成的是DOS程序,这里我们需要纯二进制代码,且起始地址为0(因为Proteus中8086复位后从FFFF0H开始执行,但通常我们设置ROM从F0000开始,将程序放在FFFF0H处,这里是一条跳转到主程序的指令)。或者,我们可以使用MASM和LINK,然后用EXE2BIN转换,但过程繁琐。 考虑到复杂度,对于初学者,建议先在EMU8086中实现软件方式的Hello World,然后在Proteus中实现简单的LED闪烁(硬件仿真),然后逐步学习串行通信。 因此,针对用户的问题,我们给出如下回答: 回答: 使用EMU8086和Proteus实现Hello World程序的方法如下: ### 一、在EMU8086中实现Hello World(软件方式) 1. 打开EMU8086,新建一个文件。 2. 输入以下汇编代码: ```assembly org 100h ; 程序从100h开始(DOS COM文件) mov dx, msg ; 将字符串的地址存入DX mov ah, 9 ; DOS中断09H功能:显示字符串 int 21h ; 调用DOS中断 mov ah, 4Ch ; DOS中断4CH功能:程序结束 int 21h ; 调用DOS中断 msg db 'Hello, World!', '$' ; 字符串以$结尾 ``` 3. 保存并编译运行。运行后,在模拟器的输出窗口(通常是模拟的DOS窗口)中可以看到字符串"Hello, World!"。 ### 二、在Proteus中实现Hello World(硬件仿真方式) 在Proteus中,我们需要构建硬件电路并编写直接控制硬件的程序。我们使用串行通信和虚拟终端来显示字符串。 1. **硬件设计**: - 打开Proteus,新建工程。 - 添加以下元件: - 8086 CPU (在Microprocessor ICs库中) - 8250 UART (在Microprocessor Peripherals库中,如果没有8250,可以用8251代替,但初始化不同) - VIRTUAL TERMINAL (在Virtual Instruments模式中) - 74LS373 (地址锁存器) 和 74LS138 (地址译码器) 等(用于最小系统,可选,为了简化,我们可以直接连接片选,但实际中需要译码) - 晶体振荡器(连接8250的XTAL1和XTAL2,频率1.8432MHz) - 连接电路: - 8086的数据总线(D0-D7)连接8250的数据线(D0-D7) - 8086的地址线A0-A2连接8250的A0-A2 - 8086的IOR#和IOW#连接8250的RD#和WR# - 8250的CS0接高电平,CS1和CS2接地(这样8250始终被选中,端口地址为0-7,实际中应该译码产生片选信号) - 8250的TX连接虚拟终端的RXD - 虚拟终端的波特率设置为9600,数据位8,无校验,停止位1(在虚拟终端的属性中设置) - 8086的复位电路和时钟电路(可放置一个DEFAULT的时钟源,频率5MHz) 注意:这是一个简化连接,实际中8250需要正确的时钟和波特率设置。 2. **编写汇编程序**: 程序需要初始化8250,然后发送字符串。以下是一个示例程序(假设8250的端口地址为0-7): ```assembly ; 初始化8250 MOV AL, 80h ; 设置DLAB=1(访问波特率除数寄存器) OUT 03h, AL ; 写入线路控制寄存器(端口3) MOV AL, 0Ch ; 波特率除数低位(9600波特率,除数=12) OUT 00h, AL MOV AL, 00h ; 波特率除数高位 OUT 01h, AL MOV AL, 03h ; 设置数据格式:8位数据,1位停止,无校验,DLAB=0 OUT 03h, AL ; 发送字符串 MOV SI, msg ; 字符串地址 NEXT: MOV AL, [SI] ; 取一个字符 CMP AL, 0 ; 检查字符串结束 JZ DONE CALL SEND_CHAR INC SI JMP NEXT DONE: HLT ; 发送字符子程序(发送AL中的字符) SEND_CHAR: PUSH AX WAIT: IN AL, 05h ; 读线路状态寄存器(端口5) AND AL, 20h ; 检查发送保持寄存器空(THRE)位 JZ WAIT ; 不空则等待 POP AX OUT 00h, AL ; 发送字符 RET msg db 'Hello, World!',0 ; 字符串以0结尾 ``` 3. **将程序加载到8086的ROM中**: - 将上述汇编代码编译成二进制文件.bin或.hex)。可以使用EMU8086(选择生成纯二进制文件)或其他汇编器。 - 在Proteus中,双击8086处理器,在“Program File”一栏选择编译好的二进制文件- 设置8086的复位地址和复位向量(通常,8086复位后从FFFF0H开始执行,因此我们需要确保程序在FFFF0H处有一条跳转指令跳转到我们的主程序。或者,我们可以将程序放在FFFF0H开始的地方,但通常需要一小段引导程序)。为了简单,我们假设程序从00000H开始执行,然后设置8086的复位地址为00000H(实际上,8086复位后从FFFF0H取第一条指令,因此我们需要在FFFF0H处放置一个远跳转指令:JMP F000:0000,然后在F0000H处开始放置我们的程序。但这样就需要设置段寄存器。另一种方法是将ROM放置在高地址区域,并在FFFF0H处放置跳转指令)。 由于设置复位向量和程序加载位置较为复杂,这里不展开。建议将程序放在00000H开始的位置,然后设置8086的复位地址为00000H(在Proteus中,可以通过设置8086的“Reset Address”属性,但默认就是FFFF0H,所以我们需要在FFFF0H处放一条跳转到00000H的指令)。具体做法是在程序的最开始(00000H)放置我们的主程序,然后在FFFF0H处放置机器码:EA 00 00 00 F0(这是JMP F000:0000的机器码,注意地址是段基址和偏移量,这里F000:0000相当于物理地址F0000,这样我们需要把程序放在F0000H开始的区域)。实际上,我们可以将整个程序放在F0000H到FFFFFH的区域,并在FFFF0H处放一个短跳转到F000:0000(物理地址F0000)的指令。 4. **运行仿真**: - 点击Proteus的运行按钮。 - 如果一切正常,虚拟终端会显示字符串"Hello, World!"。 注意:这个过程对初学者来说有一定难度,需要熟悉8086硬件系统和8250芯片的使用。如果遇到问题,建议先使用Proteus调试功能单步执行程序,观察寄存器和端口状态。 总结:对于初学者,建议先在EMU8086中实现软件方式的Hello World,掌握汇编语言和模拟器使用。然后再尝试在Proteus中构建硬件电路实现硬件方式的Hello World,以深入理解微机原理和接口技术。 §§相关问题§§ 1. 如何将EMU8086生成的程序加载到Proteus中? 2. Proteus中8086的最小系统如何搭建? 3. 8250 UART芯片的初始化步骤有哪些? 4. 在Proteus中如何调试8086汇编程序? 5. 除了串行终端,还有哪些方式在Proteus中显示字符? </think>### 使用EMU8086和Proteus实现Hello World程序的方法 #### **一、在EMU8086中实现(软件仿真)** 1. **编写汇编代码** 在EMU8086中新建文件,输入以下代码: ```assembly org 100h ; DOS兼容格式起始地址 ; 显示字符串 mov dx, msg ; DX指向字符串地址 mov ah, 09h ; DOS功能号09H(显示字符串) int 21h ; 调用DOS中断 ; 退出程序 mov ah, 4Ch ; DOS功能号4CH(退出程序) int 21h msg db 'Hello, World!', '$' ; 以$结尾的字符串 ``` 2. **运行与调试** - 点击"Emulate"按钮编译运行 - 在模拟器的输出窗口查看结果(自动显示字符串- 使用调试工具单步执行,观察寄存器变化(如DX存储字符串地址) 3. **关键点说明** - `INT 21H`是DOS系统调用,需在EMU8086的虚拟DOS环境中运行 - `$`符号是DOS字符串结束标志 - 无需硬件支持,纯软件仿真[^1] --- #### **二、在Proteus中实现(硬件仿真)** 1. **电路设计** - **元件清单**: - 8086 CPU(模式:Real 16-Bit) - 8255A PPI(并行接口芯片) - 16×2 LCD字符显示器 - 74HC373地址锁存器 - 复位电路(电阻+电容+按钮) - 10MHz时钟源 - **连接方式**: ```mermaid graph LR 8086-->|数据总线| 8255A 8086-->|地址总线| 74HC373 74HC373-->|地址译码| 8255A 8255A-->|数据线DB0-DB7| LCD 8255A-->|控制线RS/RW/E| LCD ``` 2. **汇编程序** ```assembly ; 定义端口地址 PORT_A equ 00h ; 8255A端口A PORT_B equ 02h ; 端口B PORT_C equ 04h ; 端口C CTL_REG equ 06h ; 控制寄存器 org 0000h main: ; 初始化8255A(模式0:A/B输出,C输出) mov al, 80h out CTL_REG, al ; LCD初始化序列 call lcd_init ; 显示字符串 mov si, msg call lcd_print hlt lcd_init: ... ; LCD初始化代码(设置光标/显示模式等) ret lcd_print: ... ; 循环发送字符到LCD ret msg db 'Hello World', 0 ``` 3. **操作步骤** 1.EMU8086中编译生成`.hex`文件 2. Proteus中双击8086 CPU,加载`.hex`文件 3. 设置CPU时钟频率为10MHz 4. 运行仿真,观察LCD显示结果 5. 使用调试功能跟踪指令执行[^2] --- #### **三、关键差异与注意事项** | **项目** | EMU8086方案 | Proteus方案 | |----------------|---------------------------|---------------------------| | **实现方式** | 软件中断(INT 21H) | 硬件驱动(LCD控制器) | | **输出设备** | 模拟器内置终端 | 外部LCD组件 | | **复杂度** | 简单(20行代码) | 中等(需硬件知识) | | **学习目标** | 汇编语法/DOS调用 | 硬件接口/时序控制 | | **调试重点** | 寄存器/标志位 | 信号波形/端口状态 | > **注意**:Proteus方案需严格匹配硬件时序: > - LCD初始化需15ms延时 > - 控制信号E的下降沿触发数据写入 > - 端口地址必须与电路译码一致[^2] --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逸云沙鸥のIHave@Dream

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值