汇编案例代码学习实例二

代码实例一:实现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


猜你喜欢

转载自blog.csdn.net/liudongdong19/article/details/80551697