直接定址表

一、單元長度的標號

之前的課程中,我們一直在代碼段中使用標號來標記指令、數據、段的起始地址。比如,下面的程序
將code段中的a標號處的8個數據累加,結果存儲到b標號處的字中。

assume cs:code

code segment
	a: db 1,2,3,4,5,6,7,8
    b: dw 0

  start:mov si,offset a
        mov bx,offset b
        mov cx,8
      s:mov al,cs:[si]
	    mov ah,0
        add cs:[bx],ax
		inc si
		loop s
		
		mov ax,4c00h
		int 21h
code ends
end start

程序中,code、a、b、start、s都是標號。這些標號僅僅表示了內存單元的地址。我們還可使用一種標號,
這種標號不但表示內存單元的地址,還表示了內存單元的長度,即表示在此標號處的單元是一個字節單元,
還是字單元,還是雙字單元。上面的程序還可這樣寫:

assume cs:code

code segment
	a db 1, 2, 3, 4, 5, 6, 7, 8
	b dw 0
  start:mov si,0
        mov cx,8
      s:mov al,a[si]
	    mov ah,0
        add b,ax
		inc si
		loop s
		mov ax, 4c00h
		int 21h
code ends
end start

在code段中使用的a、b後面沒有":",它們是同時描述內存地址和單元長度的標號。標號a,描述了地址
code:0,和從這個地址開始,以後的內存單元地址都是字節單元;而標號b描述了地址code:8,和從這
個地址開始,以後的內存單元都是字單元。
因爲這種標號包含了對單元長度的描述,所以在指令中,它可以代表一個段中的內存單元,比如對於程序
中的“b dw 0”

	指令 mov ax,b  相當於mov ax,cs:[8]
	指令 mov b,2   相當於 mov word ptr cs:[8],2
	指令 inc b     相當於 inc word ptr cs:[8]

對於程序中的“a db 1, 2, 3, 4, 5, 6, 7, 8”

	指令 mov al,a[si]	相當於 mov al, cs:0[si]
	指令 mov al, a[3]	相當於 mov al, cs:0[3]
	指令 mov al, a[bx+si+3]	相當於 mov al, cs:0[bx+si+3]

二、其他段中使用數據標號

一般來說,我們不在代碼段中定義數據,而是定義到其他段中,在其他段中,我們也可以使用數據標號來
描述存儲數據的單元的地址和長度。
注意,在後面有":"的地址標號,只能在代碼段中使用,不能再其他段中使用。
如下面程序

assume cs:code, es:data

data segment
	a db 1, 2, 3, 4, 5, 6, 7, 8
	b dw 0
data ends

code segment

    start: 	mov ax, data
			mov es, ax
			
			mov si, 0
			mov cx, 8
		s:	mov al, a[si]
			mov ah, 0
			add b, ax
			inc si
			loop s
			mov ax, 4c00h
			int 21h
code ends
end start

注意,如果想在代碼段中使用數據標號來訪問數據,則需要用僞指令assume將標號所在的段和一個段寄存器
聯繫起來。否則編譯器在編譯的時候,無法確定標號的段地址在哪一個寄存器中
。比如,我們在上面的程序
中要在代碼段中用data段中的數據標號a、b訪問數據,則必須用assume將一個寄存器和data段相聯。在程序
中我們用ds段寄存器和data段相聯,則編譯器對相關指令的編譯如下:
指令 mov al, a[si] 編譯爲 mov al, [si+0]
指令 add b, ax 編譯爲 add [8], ax
因爲這些實際編譯出的指令,都默認所訪問單元的段地址在ds中,而實際要訪問的段爲data,所以若要訪問
正確,在這些指令執行之前,ds中必須爲data段的段地址,則我們在程序中使用指令:
mov ax, data
mov ds, ax
設置ds指向data段。
我們也可以將標號當作數據來定義,比如:
data segment
a db 1, 2, 3, 4, 5, 6, 7, 8
b dw 0
c dw a, b ;這裏a和b相當於 offset a和offset b
d dd a, b ;相當於 offset a, seg a, offset b, seg b
data ends
seg 操作符,功能爲取得某一標號的段地址

三、直接定址表

編寫一個子程序,計算sin(x) x∈{0°, 30°, 60°, 90°, 120°, 150°, 180°},並在屏幕中間顯示結果

assume cs:code

code segment
	start:mov al, 30
		  mov ah, 0
		  
		  call showSin
		  mov ax, 4c00h
		  int 21h
		
  showSin:jmp short show
	table		dw arg0, arg30, arg60, arg90, arg120, arg150, arg180	;字符串偏移地址
	arg0		db '0', 0
	arg30		db '0.5', 0
	arg60		db '0.866', 0
	arg90		db '1', 0
	arg120		db '0.866', 0
	arg150		db '0.5', 0
	arg180		db '0', 0
  
	 show:push bx
		  push es
		  push si
		  
		  mov bx, 0b800h
		  mov es, bx
	;以下用角度/30作爲相對於table的偏移,取得對應的字符串的偏移地址,放在bx中
		  mov ah, 0
		  mov bl, 30
		  div bl
		  mov bl, al
		  mov bh, 0
		  add bx, bx
		  mov bx, table[bx]
	;以下顯示sin(x)對應的字符串
		  mov si, 160*12+40*2
	shows:mov ah, cs:[bx]
		  cmp ah, 0
		  je showret
		  mov es:[si], ah
		  inc bx
		  add si, 2
		  jmp short shows
	 
  showret:pop si
		  pop es
		  pop bx
		  ret
		  
code ends
end start 

執行結果:
在這裏插入圖片描述程序中我們通過查表的方式直接結算出索要查找的元素在表中的位置,像這種通過依據數據,直接計算出
要找的元素的位置的表,我們稱爲直接定址表,這種查找屬於典型的以空間換取效率。

四、程序入口地址的直接定址表

編程:實現一個子程序sctscreen,爲顯示輸出提供以下功能
(1)清屏;
(2)設置前景色
(3)設置背景色
(4)向上滾動一行

入口參數說明如下:
(1)用ah寄存器傳遞功能號:0表示清屏,1表示設置前景色,2表示設置背景色,3表示向上滾動一行
(2)對於1,2號功能,用al傳遞顏色值, al∈{0,1,2,3,4,5,6,7}。
程序分析:
(1)清屏:將顯存中當前屏幕中的字符設爲空格符;
(2)設置前景色:設置顯存裏當前屏幕中處於奇地址的屬性字節的第0、1、2位;
(3)設置背景色:設置現存裏當前屏幕中處於奇地址的屬性字節的第4、5、6位;
(4)向上滾動一行:依次將第n+1行的內容複製到第n行處,最後一行爲空;
完整程序如下:

assume cs:code

stack segment
	db 128 dup (0)
stack ends

code segment
	start:mov al, 1
		  mov ah, 0
		  call sctscreen
		  
		  mov ax, 4c00h
		  int 21h
		  ;org 204H  
	
	;-----------清屏----------------
	 cls: push bx
		  push cx
		  push es
		  
		  mov bx, 0b800h
		  mov es, bx
		  mov di, 0
		  mov cx, 2000
	  s1: mov byte ptr es:[di], ' '
		  add di, 2
		  loop s1
		  
		  pop es
		  pop cx
		  pop bx
		  ret

;-----------設置前景色----------------
frontColor:
		  push bx
		  push cx
		  push es
		  
		  mov bx, 0b800h
		  mov es, bx
		  mov bx, 1
		  mov cx, 2000
	  s2: and byte ptr es:[bx], 11111000B
		  or es:[bx], al
		  add bx, 2
		  loop s2
		  
		  pop es
		  pop cx
		  pop bx
		  ret
		  
	;-----------設置背景色----------------
backColor:
		  push bx
		  push cx
		  push es
		  
		  mov cl, 4
		  shl al, cl
		  
		  mov bx, 0b800h
		  mov es, bx
		  mov bx, 1
		  mov cx, 2000
	  s3: and byte ptr es:[bx], 10001111B
		  or es:[bx], al
		  add bx, 2
		  loop s3
		  
		  pop es
		  pop cx
		  pop bx
		  ret
	
	;-----------滾動一行----------------
  scroll: push cx
		  push es
		  push ds
		  push si
		  push di
		  
		  mov si, 0b800h
		  mov es, si
		  mov di, 0h
		  
		  mov ds, si
		  mov si, 160
		  
		  mov cx, 24
		  cld
	  s4: push cx
		  mov cx, 160
	  s5: rep movsb
		  pop cx
		  loop s4
		 
		  mov cx, 80
		  mov di, 0
	  s6: mov byte ptr es:[160*24 + di], ' '
		  add di, 2
		  loop s6
		  
		  pop di
		  pop si
		  pop ds
		  pop es
		  pop cx
		  ret
		  
   sctscreen: jmp short set
   table dw cls, frontColor, backColor, scroll
		  
	 set: push bx
	      cmp ah, 3	;判斷功能號是否大於3
		  ja sctscreenret
		  mov bl, ah
		  mov bh, 0
		  add bx, bx	;根據ah中的功能號計算對應子程序在table中的索引
		  call word ptr table[bx]	;調用對應的功能子程序
		  
sctscreenret: pop bx
		  ret		  
code ends
end start 

執行結果:
清屏
在這裏插入圖片描述設置前景色、設置背景色、滾動一行直接改參數編譯運行即可

結論:

用根據功能號查找地址表的方法,程序的結構清晰,便於擴充,如果新加入一個功能,只需要在地址表處添加它的入口地址就行了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章