代码实例一:实现BCD数相加
name "huge" ; this example shows how to add huge unpacked bcd numbers. ; this allows to over come the 16 bit and even 32 bit limitation. ; because 32 digit decimal value holds over 100 bits! ; with some effort the number of digits can be increased. org 100h ; skip data: jmp code ; the number of digits in numbers: ; it's important to reserve 0 as most significant digit, to avoid overflow. ; so if you need to operate with 250 digit values, you need to declare len = 251 len equ 32 ; every decimal digit is stored in a separate byte. ; first number is: 423454612361234512344535179521 num1 db 0,0,4,2,3,4,5,4,6,1,2,3,6,1,2,3,4,5,1,2,3,4,4,5,3,5,1,7,9,5,2,1 ; second number is: 712378847771981123513137882498 num2 db 0,0,7,1,2,3,7,8,8,4,7,7,7,1,9,8,1,1,2,3,5,1,3,1,3,7,8,8,2,4,9,8 ; we will calculate this: ; sum = num1 + num2 ; 423454612361234512344535179521 + 712378847771981123513137882498 = ; = 1135833460133215635857673062019 sum db len dup(0) ; declare array to keep the result. ; you may check the result on paper, or click Start , then Run, then type "calc" and hit enter key. code: nop ; entry point (nop does nothing, it's nope). ; digit pointer: mov bx,10 xor bx, bx ; setup the loop: mov cx, len mov bx, len-1 ; point to lest significant digit! next_digit: ; add digits: mov al, num1[bx] adc al, num2[bx] ; this is a very useful instruction that ; adjusts the value of addition ; to be string compatible aaa ; aaa stands for ascii add adjust. ; --- algorithm behind aaa --- ; if low nibble of al > 9 or af = 1 then: ; al = al + 6 ; ah = ah + 1 ; af = 1 ; cf = 1 ; else ; af = 0 ; cf = 0 ; ; in both cases: clear the high nibble of al. ; --- end of aaa logic --- ; store result: mov sum[bx], al ; point to next digit: dec bx loop next_digit ; include carry in result (if any): adc sum[bx], 0 ; print out the result: mov cx, len ; start printing from most significant digit: mov bx, 0 print_d: mov al, sum[bx] ; convert to ascii char: ;功能描述:在Teletype模式下显示字符 ;入口参数:AH=0EH ;AL= 字符 ;BH=页码 ;BL=前景色(图形模式) or al, 30h mov ah, 0eh int 10h inc bx loop print_d ; wait for any key press: mov ah, 0 int 16h ret ; stop
实例二:int 10h绘制图案
name "vga" ; this program draws a tiny rectangle in vga mode. org 100h jmp code ; dimensions of the rectangle: ; width: 10 pixels ; height: 5 pixels w equ 100 h equ 50 ; set video mode 13h - 320x200 code: mov ah, 0 mov al, 13h int 10h ; draw upper line: mov cx, 100+w ; column mov dx, 20 ; row mov al, 15 ; white u1: mov ah, 0ch ; put pixel int 10h dec cx cmp cx, 100 jae u1 ; draw bottom line: mov cx, 100+w ; column mov dx, 20+h ; row mov al, 15 ; white u2: mov ah, 0ch ; put pixel int 10h dec cx cmp cx, 100 ja u2 ; draw left line: mov cx, 100 ; column mov dx, 20+h ; row mov al, 15 ; white u3: mov ah, 0ch ; put pixel int 10h dec dx cmp dx, 20 ja u3 ; draw right line: mov cx, 100+w ; column mov dx, 20+h ; row mov al, 15 ; white u4: mov ah, 0ch ; put pixel int 10h dec dx cmp dx, 20 ja u4 ; pause the screen for dos compatibility: ;wait for keypress mov ah,00 int 16h ; return to text mode: mov ah,00 mov al,03 ;text mode 3 int 10h ret
实例三:显示字符
name "hi-world" ; this example prints out "hello world!" ; by writing directly to video memory. ; in vga memory: first byte is ascii character, byte that follows is character attribute. ; if you change the second byte, you can change the color of ; the character even after it is printed. ; character attribute is 8 bit value, ; high 4 bits set background color and low 4 bits set foreground color. ; hex bin color ; ; 0 0000 black ; 1 0001 blue ; 2 0010 green ; 3 0011 cyan ; 4 0100 red ; 5 0101 magenta ; 6 0110 brown ; 7 0111 light gray ; 8 1000 dark gray ; 9 1001 light blue ; a 1010 light green ; b 1011 light cyan ; c 1100 light red ; d 1101 light magenta ; e 1110 yellow ; f 1111 white org 100h ; set video mode mov ax, 3 ; text mode 80x25, 16 colors, 8 pages (ah=0, al=3) int 10h ; do it! ; cancel blinking and enable all 16 colors: mov ax, 1003h mov bx, 0 int 10h ; set segment register: mov ax, 0b800h mov ds, ax ; print "hello world" ; first byte is ascii code, second byte is color code. mov [02h], 'H' mov [04h], 'e' mov [06h], 'l' mov [08h], 'l' mov [0ah], 'o' mov [0ch], ',' mov [0eh], 'W' mov [10h], 'o' mov [12h], 'r' mov [14h], 'l' mov [16h], 'd' mov [18h], '!' ; color all characters: mov cx, 12 ; number of characters. mov di, 03h ; start from byte after 'h' c: mov [di], 11101100b ; light red(1100) on yellow(1110) add di, 2 ; skip over next ascii code in vga memory. loop c ; wait for any key press: mov ah, 0 int 16h ret
实例:; print result in binary:
name "add-sub"
org 100h
mov al, 5 ; bin=00000101b
mov bl, 10 ; hex=0ah or bin=00001010b
; 5 + 10 = 15 (decimal) or hex=0fh or bin=00001111b
add bl, al
; 15 - 1 = 14 (decimal) or hex=0eh or bin=00001110b
sub bl, 1
; print result in binary:
mov cx, 8
print: mov ah, 2 ; print function.
mov dl, 'x'
test bl, 10000000b ; test first bit.
jz zero
mov dl, '1'
zero: int 21h
shl bl, 1
loop print
; print binary suffix:
mov dl, 'b'
int 21h
; wait for any key press:
mov ah, 0
int 16h
ret
实例:计算数组的值,并以二进制和十进制打印结果
name "calc-sum" org 100h ; directive make tiny com file. ; calculate the sum of elements in vector, ; store result in m and print it in binary code. ; number of elements: mov cx, 5 ; al will store the sum: mov al, 0 ; bx is an index: mov bx, 0 ; sum elements: next: add al, vector[bx] ; next byte: inc bx ; loop until cx=0: loop next ; store result in m: mov m, al ; print result in binary: mov bl, m mov cx, 8 print: mov ah, 2 ; print function. mov dl, '0' test bl, 10000000b ; test first bit. jz zero mov dl, '1' zero: int 21h shl bl, 1 loop print ; print binary suffix: mov dl, 'b' int 21h mov dl, 0ah ; new line. int 21h mov dl, 0dh ; carrige return. int 21h ; print result in decimal: mov al, m //打印m的值 call print_al ; wait for any key press: mov ah, 0 int 16h ret ; variables: vector db 5, 4, 5, 2, 1 m db 0 print_al proc cmp al, 0 jne print_al_r push ax mov al, '0' mov ah, 0eh int 10h pop ax ret print_al_r: pusha mov ah, 0 cmp ax, 0 je pn_done mov dl, 10 div dl call print_al_r mov al, ah add al, 30h mov ah, 0eh int 10h jmp pn_done pn_done: popa ret endp
实例:体会标志寄存器与跳转指令
name "flags" org 100h ; this sample shows how cmp instruction sets the flags. ; usually cmp instruction is followed by any relative ; jump instruction such as: je, ja, jl, jae... ; it is recommended to click "flags" and "analyze" ; for better visual expirience before stepping through this code. ; (signed/unsigned) ; 4 is equal to 4 mov ah, 4 mov al, 4 cmp ah, al nop ; (signed/unsigned) ; 4 is above and greater then 3 mov ah, 4 mov al, 3 cmp ah, al nop ; -5 = 251 = 0fbh ; (signed) ; 1 is greater then -5 mov ah, 1 mov al, -5 cmp ah, al nop ; (unsigned) ; 1 is below 251 mov ah, 1 mov al, 251 cmp ah, al nop ; (signed) ; -3 is less then -2 mov ah, -3 mov al, -2 cmp ah, al nop ; (signed) ; -2 is greater then -3 mov ah, -2 mov al, -3 cmp ah, al nop ; (unsigned) ; 255 is above 1 mov ah, 255 mov al, 1 cmp ah, al nop ; now a little game: game: mov dx, offset msg1 //lea dx,msg1 mov ah, 9 ;打印字符串操作 int 21h ; read character in al: mov ah, 1 ;读取数据失败?? int 21h cmp al, '0' jb stop cmp al, '9' ja stop cmp al, '5' jb below ja above mov dx, offset equal_5 jmp print below: mov dx, offset below_5 jmp print above: mov dx, offset above_5 print: mov ah, 9 int 21h jmp game ; loop. stop: ret ; stop msg1 db "enter a number or any other character to exit: $" equal_5 db " is five! (equal)", 0Dh,0Ah, "$" below_5 db " is below five!" , 0Dh,0Ah, "$" above_5 db " is above five!" , 0Dh,0Ah, "$"
实例:计算俩数组的和
name "add-2" ; this example calculates the sum of a vector with ; another vector and saves result in third vector. ; you can see the result if you click the "vars" button. ; set elements for vec1, vec2 and vec3 to 4 and show as "signed". org 100h jmp start vec1 db 1, 2, 5, 6 vec2 db 3, 5, 6, 1 vec3 db ?, ?, ?, ? start: lea si, vec1 lea bx, vec2 lea di, vec3 mov cx, 4 sum: mov al, [si] add al, [bx] mov [di], al inc si inc bx inc di loop sum ret
实例:文件属性设置??
name "attrib" ; set and get file attributes... ; note 1: you need to manually create and copy "test.txt" to: ; c:\emu8086\vdrive\c before running this example. ; note 2: it may look like the file suddenly disappears unless ; you set the settings of file manager to show system and hidden files. ; note 3: file must exist for setting parameters. however reading ; parameters does not require the existance of a file and ; it is usually used to check if file exists or not. org 100h jmp start filename db "d:\test.txt", 0 sOK db "ok! file found. attributes set: system, hidden & read-only. $" sERR db "file does not exist. (expected i/o error)", 0dh, 0ah db ' you need to manually create and copy "test.txt" to:', 0dh, 0ah db ' "c:\emu8086\vdrive\c" before running this example.$ ' ; when running in emulator, the real path of this file is: ; c:\emu8086\vdrive\c\test.txt start: xor cx, cx ; read attributes: mov ah, 43h mov al, 0 mov dx, offset filename int 21h jc error ; set new attributes: mov ah, 43h mov al, 1 mov cx, 7 mov dx, offset filename int 21h jc error mov dx, offset sOK mov ah, 9 int 21h jmp wait_any_key error: mov dx, offset sERR mov ah, 9 int 21h wait_any_key: mov ah, 0 int 16h ret
实例:BCD_aaa 如果AL(3-0)大于9或辅助进位AF=1,则AH=AH+01H,AL=AL+06H,且置AF和CF为1;否则置AF和CF为零。AL(7-4)=0。
; this example shows the use of aaa instruction (ascii adjust after addition). ; it is used to add huge bcd numbers. name "bcd_aaa" org 100h ; first number '9': mov ah, 09h ; second number '5': mov al, 05h ; al = al + ah = ; = 09h + 05h = 0eh add al, ah ; clear tens byte of bcd ; result: xor ah, ah ; adjust result to bcd form, ; ah = 1, al = 4 -> '14' aaa ; print the result: ; store contents of ; ax register: mov dx, ax ; print first digit: mov ah, 0eh ; convert to ascii: or dh, 30h mov al, dh ;34显示的是4 int 10h ;显示al的值 ; print second digit: ; convert to ascii: or dl, 30h mov al, dl int 10h ; wait for any key press: mov ah, 0 int 16h ret ; return control to operating system.
实例:aas
AAS 减法的ASCII调整指令(ASCII Adjust for Subtraction)
若AL寄存器的低4位>9或AF=1,则:
(1)AL->AL-6,AF置1;
(2)将AL寄存器高4位清零;
(3)AH<-AH-1,CF置1。
否则,不需要调整。
; this is an example of aas instruction, ; it is used to subtract huge bcd numbers (binary coded decimals). name "bcd_aas" org 100h ; make 5 - 8 ; al = 0fdh (not in binary coded decimal form) mov al, 05h mov bl, 08h sub al, bl ; convert to binary coded decimal, ; al = 7 ; and 1 is borrowed from ah, like calculating 15 - 8: aas ; convert to printable symbol: or al, 30h ; print char in al using bios teletype function: mov ah, 0eh int 10h ; wait for any key press: mov ah, 0 int 16h ret ; return control to operating system.
实例:输入二进制转化为十进制
; input8 bit binary number and print out decimal to screen. ; zeros and ones -> decimal value ORG 100h ; macro ; this macro prints a char in AL and advances ; the current cursor position: PUTC MACRO char PUSH AX MOV AL, char MOV AH, 0Eh INT 10h POP AX ENDM .data ; null terminated input string: DB "0" s1 DB "00000000", 0 sum DW 0 ; result. flag DB 0 .code CALL print DB 0dh, 0ah, "8 bit binary: ", 0 ; get string: MOV DX, 9 ; buffer size (1+ for zero terminator). LEA DI, s1 CALL GET_STRING ; check that we really got 8 zeros and ones MOV CX, 8 MOV SI, OFFSET s1 check_s: CMP [SI], 0 JNE ok0 MOV flag, 1 ; terminated. JMP convert ok0: CMP [SI], 'b' JNE ok1 MOV flag, 1 ; terminated. JMP convert ok1: ; wrong digit? Not 1/0? CMP [SI], 31h JNA ok2 JMP error_not_valid ok2: INC SI LOOP check_s ; start the conversion from string to value in SUM variable. convert: MOV BL, 1 ; multiplier. MOV CX, SI SUB CX, OFFSET s1 DEC SI JCXZ stop_program next_digit: MOV AL, [SI] ; get digit. SUB AL, 30h MUL BL ; no change to AX. ADD SUM, AX SHL BL, 1 DEC SI ; go to previous digit. LOOP next_digit ; done! converted number is in SUM. ; check if signed TEST sum, 0000_0000_1000_0000b JNZ print_signed_unsigned print_unsigned: CALL print DB 0dh, 0ah, "decimal: ", 0 MOV AX, SUM CALL PRINT_NUM_UNS JMP stop_program print_signed_unsigned: CALL print DB 0dh, 0ah, "unsigned decimal: ", 0 ; print out unsigned: MOV AX, SUM CALL PRINT_NUM_UNS CALL print DB 0dh, 0ah, "signed decimal: ", 0 ; print out singed: MOV AX, SUM CBW ; convert byte into word. CALL PRINT_NUM JMP stop_program error_not_valid: CALL print DB 0dh, 0ah, "error: only zeros and ones are allowed!", 0 stop_program: ; wait for any key.... CALL print DB 0dh, 0ah, "press any key...", 0 MOV AH, 0 INT 16h RET ; procedures ; copied from c:\emu8086\emu8086.inc GET_STRING PROC NEAR PUSH AX PUSH CX PUSH DI PUSH DX MOV CX, 0 ; char counter. CMP DX, 1 ; buffer too small? JBE empty_buffer ; DEC DX ; reserve space for last zero. ;============================ ; loop to get and processes key presses: wait_for_key: MOV AH, 0 ; get pressed key. INT 16h CMP AL, 13 ; 'RETURN' pressed? JZ exit CMP AL, 8 ; 'BACKSPACE' pressed? JNE add_to_buffer JCXZ wait_for_key ; nothing to remove! DEC CX DEC DI PUTC 8 ; backspace. PUTC ' ' ; clear position. PUTC 8 ; backspace again. JMP wait_for_key add_to_buffer: CMP CX, DX ; buffer is full? JAE wait_for_key ; if so wait for 'BACKSPACE' or 'RETURN'... MOV [DI], AL INC DI INC CX ; print the key: MOV AH, 0Eh INT 10h JMP wait_for_key ;============================ exit: ; terminate by null: MOV [DI], 0 empty_buffer: POP DX POP DI POP CX POP AX RET GET_STRING ENDP ; copied from c:\emu8086\emu8086.inc PRINT_NUM PROC NEAR PUSH DX PUSH AX CMP AX, 0 JNZ not_zero PUTC '0' JMP printed_pn not_zero: ; the check SIGN of AX, ; make absolute if it's negative: CMP AX, 0 JNS positive NEG AX PUTC '-' positive: CALL PRINT_NUM_UNS printed_pn: POP AX POP DX RET ENDP ; copied from c:\emu8086\emu8086.inc PRINT_NUM_UNS PROC NEAR PUSH AX PUSH BX PUSH CX PUSH DX ; flag to prevent printing zeros before number: MOV CX, 1 ; (result of "/ 10000" is always less or equal to 9). MOV BX, 10000 ; 2710h - divider. ; AX is zero? CMP AX, 0 JZ print_zero begin_print: ; check divider (if zero go to end_print): CMP BX,0 JZ end_print ; avoid printing zeros before number: CMP CX, 0 JE calc ; if AX<BX then result of DIV will be zero: CMP AX, BX JB skip calc: MOV CX, 0 ; set flag. MOV DX, 0 DIV BX ; AX = DX:AX / BX (DX=remainder). ; print last digit ; AH is always ZERO, so it's ignored ADD AL, 30h ; convert to ASCII code. PUTC AL MOV AX, DX ; get remainder from last div. skip: ; calculate BX=BX/10 PUSH AX MOV DX, 0 MOV AX, BX DIV CS:ten ; AX = DX:AX / 10 (DX=remainder). MOV BX, AX POP AX JMP begin_print print_zero: PUTC '0' end_print: POP DX POP CX POP BX POP AX RET ten DW 10 ; used as divider. ENDP ; print text that follows the caller print PROC MOV CS:temp1, SI ; store SI register. POP SI ; get return address (IP). PUSH AX ; store AX register. next_char: MOV AL, CS:[SI] INC SI ; next byte. CMP AL, 0 JZ printed_ok MOV AH, 0Eh ; teletype function. INT 10h JMP next_char ; loop. printed_ok: POP AX ; re-store AX register. ; SI should point to next command after ; the CALL instruction and string definition: PUSH SI ; save new return address into the Stack. MOV SI, CS:temp1 ; re-store SI register. RET temp1 DW ? ; variable to store original value of SI register. ENDP
实例:输出显示
; output: com
org 100h
.data
msg db "Hello, World", 24h
.code
; mov ax, @data
;mov ds, ax
mov dx, offset msg ; lea dx, msg 和 mov dx, offset msg 基本上是等价的
mov ah, 9
int 21h
.exit
@是一个字符,MASM使用它与data、code等一起使用表示程序定义的数据段(data)、代码段(code)的段地址。类似“seg data”的功能。
上述指令的功能就是获得当前程序的数据段地址,并传送给数据段DS寄存器保存。这样,程序中访问存储器操作数时,默认情况下就会从DS指定的数据段进行存取。
在32位Windows中的平展存储模型中,用户不必关心段寄存器的设置问题,所以,这样的程序中就不需要上述指令。
在16位DOS平台中,需要用户程序明确设置DS,所以当程序使用数据段、需要访问数据段中的数据,往往需要设置DS。如果在16位DOS平台中,不访问数据段(或者没有数据段、或者以其他形式访问数据段的数据),可以不设置DS。如果使用MASM 6.x支持的.startup语句,该语句包含有对DS设置的功能,也不必如上设置DS。
实例:嵌套
; procedures inside another procedure. org 100h mov ax, abc mov abc_off, ax mov abc_seg, cs call far abc_off call abc1 call abc2 ret abc_off dw ? abc_seg dw ? abc proc far mov ax, -1 jmp r abc1 proc near mov ax, 1 ret endp abc2 proc near mov ax, 2 ret endp r: retf endp
实例:比较字符串是否相等,repe cmpsb/cmpsw 指令使用
; how to use cmpsb instruction to compare byte strings. name "cmpsb" org 100h ; set forward direction: ;清除方向标志,在字符串的比较,赋值,读取等一系列和rep连用的操作中, ;di或si是可以自动增减的而不需要人来加减它的值,cld即告诉程序si,di向前移动,std指令为设置方向,告诉程序si,di向后移动 cld ; load source into ds:si, ; load target into es:di: ; mov ax, cs ;CS是代码段寄存器,这个寄存器保存的是代码段的首地址 ; mov ds, ax ; mov es, ax ??作用是什么?? lea si, str1 lea di, str2 ; set counter to string length: mov cx, size ; compare until equal: ; repe是一个串操作前缀,它重复串操作指令,每重复一次ECX的值就减一 ;一直到CX为0或ZF为0时停止。 ;cmpsb是字符串比较指令,把ESI指向的数据与EDI指向的数一个一个的进行比较。 ;当repe cmpsb配合使用时就是字符串比较啦,当相同时继续比较,不同时不比较 repe cmpsb jnz not_equal ; "yes" - equal! mov al, 'y' mov ah, 0eh int 10h jmp exit_here not_equal: ; "no" - not equal! mov al, 'n' mov ah, 0eh int 10h exit_here: ; wait for any key press: mov ah, 0 int 16h ret ; strings must have equal lengths: x1: str1 db 'test string' str2 db 'test string' size = ($ - x1) / 2
实例:加载double 32位数据
org 100h jmp a ; double word definition is supported: mydouble dd 12345678h ; it is equal to: mywords dw 5678h dw 1234h ; and it is equal to: mybytes db 78h db 56h db 34h db 12h ; exactly 32 bits binn dd 00010010001101000101011001111000b ; load double word to dx:ax a: mov ax, binn mov dx, [binn+2] ret
实例:远程调用int 10h中断
name "callfar" ; examples shows how to call int 10h without using int instruction. org 100h ; set es:bx to point to int 10h vector in interrupt vector table mov bx, 0h mov es, bx mov bx, 40h mov ah, 0eh ; set up int 10h params mov al, '*' pushf call far es:[bx] ; do a far cal to int10h vector ; wait for any key.... mov ah, 0 int 16h ret
实例:虚拟设备点亮led灯
; this example shows how to access virtual ports (0 to 65535).
; these ports are emulated in this file: c:\emu8086.io
; this technology allows to make external add-on devices
; for emu8086, such as led displays, robots, thermometers, stepper-motors, etc... etc...
; anyone can create an animated virtual device.
; c:\emu8086\devices\led_display.exe
#start=led_display.exe#
#make_bin#
name "led"
mov ax, 1234
out 199, ax
mov ax, -5678
out 199, ax
; Eternal loop to write
; values to port:
mov ax, 0
x1:
out 199, ax
inc ax
jmp x1
hlt
实例:嵌套循环
name "loops" org 100h mov bx, 0 ; total step counter mov cx, 5 k1: add bx, 1 mov al, '1' mov ah, 0eh int 10h push cx mov cx, 5 k2: add bx, 1 mov al, '2' mov ah, 0eh int 10h push cx mov cx, 5 k3: add bx, 1 mov al, '3' mov ah, 0eh int 10h loop k3 ; internal in internal loop. pop cx loop k2 ; internal loop. pop cx loop k1 ; external loop. ; wait any key... mov ah, 1 int 21h ret
实例:交通灯 虚拟端口
; controlling external device with 8086 microprocessor. ; realistic test for c:\emu8086\devices\Traffic_Lights.exe #start=Traffic_Lights.exe# name "traffic" mov ax, all_red out 4, ax mov si, offset situation next: mov ax, [si] out 4, ax ; wait 5 seconds (5 million microseconds) mov cx, 4Ch ; 004C4B40h = 5,000,000 mov dx, 4B40h mov ah, 86h int 15h add si, 2 ; next situation cmp si, sit_end jb next mov si, offset situation jmp next ; FEDC_BA98_7654_3210 situation dw 0000_0011_0000_1100b s1 dw 0000_0110_1001_1010b s2 dw 0000_1000_0110_0001b s3 dw 0000_1000_0110_0001b s4 dw 0000_0100_1101_0011b sit_end = $ all_red equ 0000_0010_0100_1001b
实例:定时显示字符
; this sample shows the use of a timer function (int 15h / 86h) ; this code prints some chars with 1 second delay. ; note: Windows XP does not support this interrupt (always sets CF=1), ; to test this program in real environment write it to a floppy disk using ; compiled writebin.asm. after sucessfull compilation of both files, ; type this from command prompt: writebin timer.bin ; note: floppy disk boot record will be overwritten. ; the floppy will not be useable under windows/dos until ; you reformat it, data on floppy disk may be lost. ; use empty floppy disks only. name "timer" #make_boot# org 7c00h ; set the segment registers mov ax, cs mov ds, ax mov es, ax call set_video_mode call clear_screen ;清屏操作 next_char: cmp count, 0 jz stop ; print char: mov al, c1 mov ah, 0eh int 10h ;输出字符设置 ; next ascii char: inc c1 dec count ; set 1 million microseconds interval (1 second) ;设置时间延迟 mov cx, 0fh mov dx, 4240h mov ah, 86h int 15h ; stop any error: jc stop jmp next_char stop: ; print message using bios int 10h/13h function mov al, 1 mov bh, 0 mov bl, 0010_1111b mov cx, msg_size mov dl, 4 mov dh, 15 mov bp, offset msg mov ah, 13h int 10h ; wait for any key... mov ah, 0 int 16h int 19h ; reboot count db 10 c1 db 'a' msg db "remove floppy disk and press any key to reboot..." msg_size = $ - msg ; set video mode and disable blinking (for compatibility). set_video_mode proc mov ah, 0 mov al, 3 ; text mode 80x25, 16 colors, 8 pages int 10h ; blinking disabled for compatibility with dos, ; emulator and windows prompt do not blink anyway. mov ax, 1003h mov bx, 0 ; disable blinking. int 10h ret set_video_mode endp ; clear the screen by scrolling entire screen window, ; and set cursor position on top. ; default attribute is changed to black on white. clear_screen proc near push ax ; store registers... push ds ; push bx ; push cx ; push di ; mov ax, 40h mov ds, ax ; for getting screen parameters. mov ah, 06h ; scroll up function id. mov al, 0 ; scroll all lines! mov bh, 1111_0000b ; attribute for new lines. mov ch, 0 ; upper row. mov cl, 0 ; upper col. mov di, 84h ; rows on screen -1, mov dh, [di] ; lower row (byte). mov di, 4ah ; columns on screen, mov dl, [di] dec dl ; lower col. int 10h ; set cursor position to top ; of the screen: mov bh, 0 ; current page. mov dl, 0 ; col. mov dh, 0 ; row. mov ah, 02 int 10h pop di ; re-store registers... pop cx ; pop bx ; pop ds ; pop ax ; ret clear_screen endp
实例:将字符串都变为大写
; this is a program in 8086 assembly language that ; accepts a character string from the keyboard and ; stores it in the string array. the program then converts ; all the lower case characters of the string to upper case. ; if the string is empty (null), it doesn't do anything. name "upper" org 100h jmp start ; first byte is buffer size, ; second byte will hold number ; of used bytes for string, ; all other bytes are for characters: string db 20, 22 dup('?') new_line db 0Dh,0Ah, '$' ; new line code. start: ; int 21h / ah=0ah - input of a string to ds:dx, ; fist byte is buffer size, second byte is number ; of chars actually read. does not add '$' in the ; end of string. to print using int 21h / ah=09h ; you must set dollar sign at the end of it and ; start printing from address ds:dx + 2. lea dx, string mov ah, 0ah int 21h mov bx, dx mov ah, 0 mov al, ds:[bx+1] add bx, ax ; point to end of string. mov byte ptr [bx+2], '$' ; put dollar to the end. ; int 21h / ah=09h - output of a string at ds:dx. ; string must be terminated by '$' sign. lea dx, new_line mov ah, 09h int 21h lea bx, string mov ch, 0 mov cl, [bx+1] ; get string size. jcxz null ; is string is empty? add bx, 2 ; skip control chars. upper_case: ; check if it's a lower case letter: cmp byte ptr [bx], 'a' jb ok cmp byte ptr [bx], 'z' ja ok ; convert to uppercase: ; upper case letter do not have ; third bit set, for example: ; 'a' : 01100001b ; 'a' : 01000001b ; upper case mask : 11011111b ; clear third bit: and byte ptr [bx], 11011111b ;将小写变为大写 ok: inc bx ; next char. loop upper_case ; int 21h / ah=09h - output of a string at ds:dx. ; string must be terminated by '$' sign. lea dx, string+2 mov ah, 09h int 21h ; wait for any key press.... mov ah, 0 int 16h null: ret ; return to operating system.
实例:统计敲击键盘次数
; Count number of key presses. the result is in bx register.
;
; You must type into the emulator's screen,
; if it closes, press screen button to re-open it.
name "keycount"
org 100h
; print welcome message:
mov dx, offset msg
mov ah, 9
int 21h
xor bx, bx ; zero bx register.
wait: mov ah, 0 ; wait for any key....
int 16h
cmp al, 27 ; if key is 'esc' then exit.
je stop
mov ah, 0eh ; print it.
int 10h
inc bx ; increase bx on every key press.
jmp wait
; print result message:
stop: mov dx, offset msg2
mov ah, 9
int 21h
mov ax, bx
call print_al
;call print_ax
; wait for any key press:
mov ah, 0
int 16h
ret ; exit to operating system.
msg db "I'll count all your keypresses. press 'Esc' to stop...", 0Dh,0Ah, "$"
msg2 db 0Dh,0Ah, "recorded keypresses: $"
print_ax proc
cmp ax, 0
jne print_ax_r
push ax
mov al, '0'
mov ah, 0eh
int 10h
pop ax
ret
print_ax_r:
pusha
mov dx, 0
cmp ax, 0
je pn_done
mov bx, 10
div bx
call print_ax_r
mov ax, dx
add al, 30h
mov ah, 0eh
int 10h
jmp pn_done
pn_done:
popa
ret
endp
print_al proc ;以十进制方式输入
cmp al, 0
jne print_al_r
push ax
mov al, '0'
mov ah, 0eh
int 10h
pop ax
ret
print_al_r:
pusha
mov ah, 0
cmp ax, 0
je pn_done
mov dl, 10
div dl
call print_al_r
mov al, ah
add al, 30h
mov ah, 0eh
int 10h
jmp pn_done
pn_done:
popa
ret
endp
实例:体会stack的功能
; this sample shows how the stack works. ; click 'stack' button in emulator to see the contents of the stack. ; stack is important element in computer architecture. ; this code does nothing useful, except printing "Hi" in the end. name "stack" org 100h ; create tiny com file. mov ax, 1234h push ax mov dx, 5678h push dx pop bx pop cx ; function call pushes ip value of the next instruction: call tfunc mov ax, 7890h push ax pop bx ; interrupts are like funtions, ; but in addition they push code segment into the stack mov ax, 3 int 10h ; set standart video mode. ; a typical use of stack is to set segment registers. ; set ds to video memory segment: mov ax, 0b800h push ax pop ds ; print "hi": mov [170h], 'H' mov [172h], 'i' ; color attribute for 'h' mov [171h], 11001110b ; color attribute for 'i' mov [173h], 10011110b ; wait for any key press.... mov ah, 0 int 16h ; here we "pop" the ip value, ; and return control to the operating system: ret ; the test procedure: tfunc proc xor bx, bx xor cx, cx ; here we "pop" the ip value, ; and return control to the main program: ret endp