實驗任務1
代碼
assume cs:code, ds:data
data segment
x db 1,9,3
len1 equ $-x
y dw 1,9,3
len2 equ $-y
data ends
code segment
start:
mov ax,data
mov ds,ax
mov si,offset x
mov cx,len1
mov ah,2
s1: mov dl,ds:[si]
or dl,30h
int 21h
mov dl,' '
int 21h
inc si
loop s1
mov ah,2
mov dl,0ah
int 21h
mov si,offset y
mov cx,len2/2
mov ah,2
s2: mov dx,ds:[si]
or dl,30h
int 21h
mov dl,' '
int 21h
add si,2
loop s2
mov ah,4ch
int 21h
code ends
end start
代碼功能簡析
輸出兩行1 9 3
or 30h
是為了轉換為\(ASCII\)碼輸出。 \(30h\) 是ASCII中 \('0'\) 的編號,其二進制形式為:\(0011 0000\) ,所以or上一個 \(30h\) 表示輸出從'0'開始偏移量為1 9 3的數字
如果 or 61h
,則輸出的是偏移量減去 \(1\) 的小寫字母。( \(61h\) 是 \(0110 0001\) ,從1開始)
$
是預定義符號,表示當前的偏移地址,使用 jmp $
,可以進入死循環。
問題1
反匯編查看機器碼,可以看到其機器碼為 \(E2F2\)
\(E2\) 表示LOOP
\(F0\) 是補碼形式的位移量, \(F2\) 轉換為二進制為 \(11110010\)
將其轉換為原碼 \(1!(1110010-1) = 1!(1110001) = 10001110 = -8+-4+-2=-14\)
所以其位移量為 \(-14\) 。當前ip為 \(0x19\) ,即 \(25\) ,\(25-14=11\)
但是s1處的偏移量為 \(D\) ,即 \(13\) 。
所以整個轉移的過程如下
ip指向 \(25\) 偏移處的指令,先取指令,然后ip自動 \(+2\) ,變為 \(27\) ,然后執行指令的過程中,將ip減去 \(14\) ,得到 \(13\) ,那么下一條要執行的指令就在偏移量為 \(13\) 的地方,即s1標號處。
問題2
重復問題1的操作步驟。
\(F0\) 的二進制形式為: \(11110000\)
得到其原碼: \(1!(1110000-1) = 1!(1101111) = 10010000 = -16\)
為什么指令數量相同,但是位移量不同?原因出在inc
和add
的指令占字節數不同。8086匯編采用動態指令長度,所以每條指令的長度都不是相同的。所以這里會有位移量的不同。
CPU計算得到s2之后指令的地址方式和問題1一樣,這里不重復描述。
實驗任務2
代碼簡析
assume cs:code, ds:data
data segment
dw 200h,0h,230h,0h
data ends
stack segment
db 16 dup(0)
stack ends
code segment
start:
mov ax,data
mov ds,ax
mov word ptr ds:[0],offset s1
mov word ptr ds:[2],offset s2
mov ds:[4],cs
mov ax,stack
mov ss,ax
mov sp,16
call word ptr ds:[0]
;push ip
;jmp word ptr ds:[0]
s1: pop ax
;(ax) = stack.top() = offset line23 + size(line23) = offset s1
call dword ptr ds:[2]
;push cs
;push ip
;jmp dword ptr ds:[2]
s2: pop bx
;(bx) = stack.top() = offset line28 + size(line28) = offset s2
pop cx
;(bx) = stack.top() = cs = code
mov ah,4ch
int 21h
code ends
end start
問題1
(ax) = offset s1
(bx) = offset s2
(cx) = cs = code
問題2
進入debug進行驗證。
驗證AX寄存器
驗證BX寄存器
驗證CX寄存器
實驗任務3
實現代碼
assume cs:code, ds:data
data segment
x db 99, 72, 85, 63, 89, 97, 55
len equ $ - x
data ends
code segment
start:
mov ax,data
mov ds,ax
mov byte ptr ds:[len],10
mov cx,7
mov bx,0
s: mov al,ds:[bx]
mov ah,0
inc bx
call printNumber
call printSpace
loop s
mov ah,4ch
int 21h
printNumber:
div byte ptr ds:[len]
mov dx,ax
mov ah,2
or dl,30h
int 21h
mov ah,2
mov dl,dh
or dl,30h
int 21h
ret
printSpace:
mov dl,' '
mov ah,2
int 21h
ret
code ends
end start
使用call和ret實現子程序編寫與調用。因為mov ah,2,int21h一次只能輸出一個字符,所以采用除10將十位與個位分開輸出。十位是商,個位是余數。ah保存余數,al保存商。
實現結果
實驗任務4
代碼
assume cs:code,ds:data
data segment
str db 'try'
len equ $ - str
data ends
stack segment
db 16 dup(0)
stack ends
code segment
start:
;F00-F9F Last
;000-09F Fisrt
;cx len
;bl color
;bh line
;ds:si fist value of one string's address
mov ax,data
mov ds,ax
; mov byte ptr ds:[len],160
mov ax,stack
mov ss,ax
mov sp,16
mov bl,00000010B
mov bh,0
mov cx,3
mov si,0
call printStr
mov bl,00000100B
mov bh,24
mov cx,3
mov si,0
call printStr
mov ah,4ch
int 21h
printStr:
mov ax,0b800h;顯存地址
mov es,ax
mov ax,0
mov al,bh;行號
mov dx,160
mul dx
mov di,ax
s: mov al,ds:[si]
mov es:[di],al
inc si
inc di
mov es:[di],bl
inc di
loop s
ret
code ends
end start
運行結果
實驗任務5
代碼
assume cs:code,ds:data
data segment
stu_no db '201983290202'
len equ $ - stu_no;學號的長度
len1 equ 40-len/2;-的長度
data ends
stack segment
db 16 dup(0)
stack ends
code segment
start:
mov ax,data
mov ds,ax
mov bl,00010001B
call setBgColor
mov bl,00010111B
call printID
mov ah,4ch
int 21h
printID:
mov ax,0b800h
mov es,ax
mov ax,0
mov al,24
mov dx,0
mov dl,160
mul dl
mov di,ax
mov cx,len1
s1: mov es:[di],2dh
mov es:[di+1],bl
add di,2
loop s1
mov si,0
mov cx,len
s2: mov al,ds:[si]
mov es:[di],al
mov es:[di+1],bl
add di,2
inc si
loop s2
mov cx,len1
s3: mov es:[di],2dh
mov es:[di+1],bl
add di,2
loop s3
ret
setBgColor:
;bl,顏色
mov ax,0b800h
mov es,ax
mov di,0
mov ax,0
mov al,25
mov dx,0
mov dl,80
mul dl
mov cx,ax
mov al,20h
s:
mov es:[di],al
inc di
mov es:[di],bl
inc di
loop s
ret
code ends
end start
運行結果
實驗總結
本次實驗學習了跳轉指令的編程以及子程序的編寫,了解了loop的原理以及call和ret的原理
還學會了向顯存中寫入內容以格式化輸出字符
一個字符占兩個字節
低位字節是要輸出的字符,高位字節是字符的格式