【8086匯編入門】《零基礎入門學習匯編語言》第四版

1基礎知識
機器語言是機器指令的集合,由0和1組成,但是很長很復雜,匯編語言因此產生。
匯編語言的主體是匯編指令。匯編指令是機器指令的便于記憶的書寫格式。
程序員寫完匯編指令通過編譯器轉換為機器碼,機器碼再傳到計算機執(zhí)行。

匯編語言有以下三類:
1匯編指令:助記符,有對應機器碼
2.偽指令:沒有對應機器碼,編譯器執(zhí)行計算機不執(zhí)行
3.其他符號:+ -等由編譯器識別,沒有對應機器碼
匯編語言的核心是匯編指令,決定了匯編語言的特性

CPU是計算機的核心部件,他控制整個計算機的運作并運算,指令和數(shù)據(jù)在存儲器中存放,也就是內存。CPU離不開內存。內存中指令和數(shù)據(jù)沒區(qū)別,都是二進制。CPU來識別是信息還是指令。
一個存儲單元存儲1Byte

CPU從內存中讀寫書,要指定地址,指定進行哪種操作,CPU通過總線連接其他芯片,傳輸信息
存儲單元的地址(地址信息)->地址總線
器件的選擇,讀或寫的命令(控制信息)->控制總線
讀或寫的數(shù)據(jù)(數(shù)據(jù)信息)->數(shù)據(jù)總線

ima111ng

地址總線
一根導線有兩種穩(wěn)定狀態(tài)代表0和1,那么10根導線就有2^10次方個不同數(shù)據(jù),從0到1023
地址總線的寬度決定了CPU的尋址能力
數(shù)據(jù)總線
8根數(shù)據(jù)總線可傳送一個8位二進制數(shù)據(jù) 8bits = 1byte
數(shù)據(jù)總線的寬度決定了CPU與其他器件進行數(shù)據(jù)傳送時的一次數(shù)據(jù)量
控制總線
控制總線的寬度決定CPU對外部器件的控制能力

主板上都是核心器件,CPU、存儲器等,CPU通過總線向接口卡發(fā)送指令,接口卡控制外設進行工作
隨機存儲器RAM 只讀存儲器ROM
BIOS(Basic input/putput system)

1

CPU將各類存儲器看作一個邏輯存儲器,所有的物理存儲器被看作一個由若干個存儲單元組成的邏輯存儲器,每個物理存儲器在這個邏輯存儲器中占有一個地址段,即一段地址空間。

ima11333ng

內存地址空間的大小受CPU地址總線寬度的限制。不同計算機系統(tǒng)內存地址分配情況不同

2.寄存器
CPU由運算器、控制器、寄存器等器件構成,器件靠內部總線連,與之前總線(外)不同
寄存器程序員可以用指令讀寫
8086CPU的14個寄存器
AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW
通用寄存器:
用來存放一般性數(shù)據(jù) AX BX CX DX
16位寄存器可以拆成兩個8位寄存器使用 AH AL,高8位和低8位

1

兩字節(jié)byte構成一個字word,一個字節(jié)8bit放在8位寄存器
匯編指令
mov ax,18
mov ah,78
add ax,8
mov ax,bx
add ax,bx
...
16位寄存器只能存放4位十六進制數(shù)據(jù),1044CH最高位的1就不能保存再ax中004CH
獨立使用AL這個寄存器如果進位是不會儲存在AH中
數(shù)據(jù)傳送和運算時 指令的兩個操作對象的位數(shù)應當一致

物理地址
所有內存單元構成的存儲空間是一個一維的線性空間,每一個內存單元在這個空間中都有唯一的地址
CPU向地址總線發(fā)出物理地址前,要在內部形成這個物理地址
16位的CPU能夠一次性處理、傳輸、暫時存儲16位地址
8086CPU有20位地址總線,內部用兩個16位地址合成一個20位 的物理地址

1

物理地址 = 段地址 x 16(基礎地址) + 偏移地址
段地址 x 16 常用說法左移4位(二進制位)相當于16進制左移1位
X 進制左移1位相當于乘 X

1

因為內部結構是這樣,為了達到20位尋址能力,利用兩個16位地址可以達到目的

1

接著段地址,內存沒有分段,段的劃分來自cpu
段地址 x 16是16的倍數(shù),所以一個段的起始地址也是16的倍數(shù);偏移地址位16位,16位地址的尋址能力位64kb,所以一個段的長度最大為64kb
CPU可以用不同的段地址和偏移地址形成同一個物理地址

8086的4個段寄存器CS、DS、SS、ES。當訪問內存由這四個段寄存器提供內存單元段地址
CS為代碼段寄存器 IP為指令指針寄存器
任意時刻,設CS內容為M,IP中內容為N,8086CPU從內存M x 16 + N單元開始,讀取指令并執(zhí)行
讀取一條指令后,IP中的值自動增加(指令長度),以使CPU可以讀取下一條指令
CPU將CS:IP指向的內存地址單元內容看作指令
同時修改CS、IP的內容可以用 jmp 段地址 :偏移地址
僅修改IP可用 jmp 某一合法寄存器(用寄存器中的值修改IP)?

8086機編程時,可以根據(jù)需要,將一組內存單元定義為一個段。
將長度為N<=64的代碼,存在一組地址連續(xù)、起始地址為16的倍數(shù)的內存單元作代碼段
用CS:IP指向的內容就能讓代碼段的內容執(zhí)行

3.寄存器(內存訪問)
在內存中存儲時,內存單元是一個字節(jié)byte單元,則一個字Word要用兩個地址連續(xù)的內存單元來存放,低位字節(jié)存放在低地址單元,高位字節(jié)存放在高地址單元
字單元:由兩個地址連續(xù)的內存單元組成,起始地址為N的字單元簡稱為N地址字單元

1

0地址字單元4E21H,1地址字單元124EH......
DS和[address]
8086中有一個DS寄存器,通常用來存放要訪問數(shù)據(jù)的段地址
mov bx,1000H
mov ds,bx
mov al,[0]
將10000H(1000:0)中的數(shù)據(jù)讀到al中
這里[...]表示一個內存單元,括號里面表示偏移地址,指令執(zhí)行時8086CPU自動讀取ds中數(shù)據(jù)作為內存單元的段地址
8086CPU不支持將數(shù)據(jù)直接送入段寄存器,只好用一個寄存器中轉
16位結構,有16根數(shù)線,所以一次可以傳送16位數(shù)據(jù),也就是一個字

mov add sub都是帶有兩個操作對象的指令,而jump具有一個

編程時根據(jù)需要定義數(shù)據(jù)段,可以在具體操作的時候用ds存放數(shù)據(jù)段的段地址,再根據(jù)需要,用相關指令訪問數(shù)據(jù)段中的具體單元

棧是一種具有特殊訪問方式(最后進入空間的數(shù)據(jù),最先出去)的存儲空間

1
1

棧的兩個基本操作:入棧和出棧,入棧就是將新的元素放到棧頂,出棧就是將棧頂元素取出一個
棧的操作規(guī)則被稱為:LIFO(Last In First Out,后的進先出來)
編程時,可以將一段內存當作棧
push ax 將ax中數(shù)據(jù)入棧
pop ax 從棧頂取出數(shù)據(jù)到ax
操作都是以字為單位進行的
8086CPU中任意時刻,段寄存器SS(棧的段地址):寄存器SP(偏移地址)指向棧頂元素,push指令和pop指令執(zhí)行時,CPU從SS和SP中得到棧頂?shù)刂?br> push ax兩步走
1> SP=SP-2,SS:SP指向當前棧頂前面的單元,作為新棧頂
2> 將ax中的內容送入SS:SP指向的內存單元處,SS:SP此時指向新棧頂
入棧時,棧頂從高地址向低地址方向增長。
pop ax兩步走
1> 將SS:SP指向的內存單元處數(shù)據(jù)送入ax
2> SP=SP+2,SS:SP指向當前棧頂下面的單元,作為新的棧頂
值得注意的時,出棧后pop操作前的棧頂元素仍然存在,但是它已經不在棧中,再次push后會在那里寫入新數(shù)據(jù)覆蓋

由上面數(shù)據(jù)不在棧中進而可以思考棧頂超界的問題
8086CPU不保證我們對棧的操作不會超界
當我們把一段內存當作??臻g,當棧滿時再執(zhí)行push棧頂超出??臻g,??臻g外數(shù)據(jù)被覆當棧空時再次執(zhí)行pop棧頂超出了??臻g,而超出的地方的數(shù)據(jù)會被覆蓋,自己需要注意
用棧暫存以后需要恢復寄存器中的內容時,出棧順序和入棧順序相反
push和pop實質上是一種內存?zhèn)魉椭噶?br> 編程時根據(jù)需要可定義棧段

段的綜述
對于數(shù)據(jù)段,將它的段地址放在DS中,用mov、add、sub等訪問內存單元的指令時,CPU就將我們定義的數(shù)據(jù)段中內容當作數(shù)據(jù)來訪問
對于代碼段,將它的段地址放在CS中,將段中第一條指令的偏移地址放在IP中,這樣CPU就將執(zhí)行我們定義的代碼段中的指令
對于棧段,將它的地址放在SS中,將棧頂單元的偏移地址放在SP中,這樣CPU進行棧操作時,將我們定義的棧段當作??臻g用
由此可見CPU將內存中內容當作什么,是因為相應的段寄存器指向了那里

4.第一個程序
一個源程序從寫出到執(zhí)行的過程
第一步:編寫匯編源程序
第二步:對源程序進行編譯鏈接
使用匯編語言編譯程序對源程序文件中的源程序進行編譯,產生目標文件;再用鏈接程序對目標文件進行鏈接,生成可在操作系統(tǒng)中直接運行的可執(zhí)行文件。
可執(zhí)行文件包含兩部分內容
1)程序(從源程序中的匯編指令翻譯過來的機器碼)和數(shù)據(jù)(源程序定義的數(shù)據(jù))
2)相關的的描述信息(比如,程序多大、占多少內存空間等)
第三步:執(zhí)行可執(zhí)行文件中的程序
操作系統(tǒng)依照可執(zhí)行文件的描述信息,將可執(zhí)行文件中的機器碼和數(shù)據(jù)載入內存,開始相關初始化,然后由CPU執(zhí)行

源程序
匯編語言寫的源程序,包括偽指令和匯編指令,其中偽指令由編譯器來處理,程序是指源程序中由計算機執(zhí)行、處理的指令或數(shù)據(jù)
偽指令:
1>segment 和 ends成對使用,功能是定義一個段,segment 說明一個段開始,ends 說明一個段結束
格式: 段名 segment
.
段名 ends
2>end是一個匯編程序的結束標志,編譯器碰到它就結束編譯,注意區(qū)分ends
3>assume 含義為假設。它假設某一段寄存器和程序中的某一個用segment...ends定義的段相關聯(lián)。
比如code segment ... code ends就定義了一個名為code的段
在程序開頭,用assume cs:code 將用作代碼段的段code和CPU中的段寄存器cs 聯(lián)系起來

DOS(一個單任務操作系統(tǒng))
一個程序p2在可執(zhí)行文件中,則必須有一個正在運行的程序p1,將p2從可執(zhí)行文件中加載入內存,將CPU的控制權交給p2,p2才能運行。p2開始運行后,p1暫停運行
而當p2運行完,CPU控制權應交還給p1
這個過程叫做:程序返回
任何通用的操作系統(tǒng),都要提供一個稱為shell(外殼)的程序,用戶使用這個程序來操作計算機系統(tǒng)進行工作
DOS啟動時,先完成其他重要初始化工作,然后運行command.com,command.com運行后,執(zhí)行完其他的相關任務后,在屏幕上顯示出由當前盤符和當前路徑組成的提示符
比如C:\
用戶輸入的指令 cd dir 等由command執(zhí)行
執(zhí)行一個程序,command 首先根據(jù)文件名找到可執(zhí)行文件,然后將可執(zhí)行文件加載入內存,設置CS:IP指向程序的入口。此后,command 暫停運行,CPU運行程序。程序運行結束后,返回到command中,command 再次顯示由當前盤符和當前路徑組成的提示符,等待用戶輸入

在DEBUG中,command將debug加載入內存,而debug將程序加載入內存,所以程序結束后返回到debug中,Q可以返回到command
mov ax,4c00h
int 21h
這兩條指令所實現(xiàn)的就是程序返回,在程序末尾使用

edit
編輯程序
masm
匯編編譯器,接收默認文件擴展名為 .asm,如果不是就要將文件擴展名寫出
輸入源程序文件名要指明路徑,除非它就在當前路徑下
masm 1t.asm / masm 1t
簡化過程,最后加上 ; 忽略中間文件的生成
link
鏈接器,接收默認文件擴展名 .obj,如果不是就要將文件擴展名寫出
對編譯生成的目標文件進行鏈接,從而得到可執(zhí)行程序。 輸入目標文件名要指明路徑,除非它就在當前路徑下
link 1t.obj / link 1t
簡化過程,最后加 ; 忽略中間文件的生成
學習匯編主要目的,通過用匯編語言進行編程而深入地理解計算機底層的基本工作機理,達到可以隨心所欲控制計算機的目的。匯編語言編程用到的工具在操作系統(tǒng)是運行,暫時不做過多探究

1

Debug
數(shù)據(jù)在Debug中默認所有數(shù)據(jù)用十六進制表示
遇到int 21h時要用P命令執(zhí)行
載入.EXE
DOS系統(tǒng)中.EXE文件中程序加載,cx 中存放了程序的長度
.exe裝入內存后,程序被裝入內存的什么地方?

1

5.[BX]和loop指令
1.bx
用[0]表示一個內存單元時,0表示單元的偏移地址,段地址默認在ds中,單元的長度(類型)可以由具體指令中的其他操作對象(比如寄存器)中指出
[bx]同樣也表示一個內存單元,它的偏移地址在bx中
inc bx的含義是bx中的內容加1,執(zhí)行后bx = 2
2.loop
正如它的意思循環(huán)
loop指令的格式是: loop 標號
CPU執(zhí)行 loop指令時兩步走(這里圓括號代表一個寄存器或內存單元的內容)
1>(cx)=(cx)-1
2>判斷cx中的值,不為零則轉至標號處執(zhí)行程序,如果為零則向下執(zhí)行
通常我們用loop指令來實現(xiàn)循環(huán)功能,cx中存放循環(huán)次數(shù)
程序框架如下
mov cx,循環(huán)次數(shù)
s:
循環(huán)執(zhí)行的程序段
loop s
調試/執(zhí)行程序時
大于9FFFh的十六進制數(shù)據(jù)A000H、A001H...FFFFH在書寫時以字母開頭,但在匯編源程序中,數(shù)據(jù)不能以字母開頭,所以前面要加0,比如0A001H

在程序執(zhí)行時 loop s 中的標號 s 已經變?yōu)榱艘粋€地址

我們只想跟蹤循環(huán)的過程時,可以用DUBUG里命令G來達到目的,一次執(zhí)行完標號前的內容,g 0012表示執(zhí)行程序到當前代碼段(段地址在cs中)的0012h處
當進入循環(huán)后,我們想要循環(huán)一次執(zhí)行完,可以用p命令來達到目的,DEBUG就會自動重復執(zhí)行循環(huán)中指令,直到(cx)=0為止
Debug和匯編編譯器masm對指令不同處理
在Debug中,mov ax,[0] 表示將ds:0處的數(shù)據(jù)送入ax中
但在匯編源程序中,這個指令被編譯器當作指令mov ax,0處理
Dubug將它解釋為idata是一個內存單元
編譯器將[idata]解釋為 idata
目前的方法是將偏移地址送入bx寄存器中,用[bx]的方式來訪問內存單元
但是這樣比較麻煩,還有一種方法是在[ ]的前面顯式地給出段地址所在的段寄存器
段前綴
用于顯式地指明內存單元的段地址的ds: cs: ss: es:,在匯編語言中成為段前綴

比如訪問2000:0單元
mov ax,2000h
mov ds,ax
mov al,ds:[0]
loop和[bx]的聯(lián)合應用
在實際編程中,經常會遇到用同一種方法處理地址連續(xù)的內存單元中的數(shù)據(jù)問題。我們需要每次循環(huán)的時候,按照同一種方法來改變要訪問的內存單元的地址
mov al,[bx] 中bx就可以看作一個代表內存單元地址的變量,我們可以通過改變 bx 中的數(shù)值,改變訪問的內存單元

計算ffff:0~ffff:b單元中的數(shù)據(jù)和,結果存儲在ds中
1.運算和的結果是字節(jié)型數(shù)據(jù),范圍在0-255之間,12個結果相加不會大于65535,dx能放下
2.不能將ffff:0~ffff:b中的數(shù)據(jù)累加到ds中,在這里面數(shù)據(jù)是8位的,不能直接加到16位寄存器dx中,也不能累加到dl中,dl會進位丟失
解決方案
用一個16位寄存器做中介,將內存單元中的8位數(shù)據(jù)賦值到一個16位寄存器ax中,再將ax中的數(shù)據(jù)加到dx上,從而使兩個運算對象的類型匹配并且結果不會超界

assume cs:code
code segment
mov ax,0ffffh
mov ds,ax
mov bx,0

mov dx,0

mov cs,12
s:
mov al,[bx]
mov ah,0
add dx,ax
inc bx
loop s

mov ax,4c00h
int 21h

code ends
end

一段安全的空間
8086模式中,隨意向一段空間寫入內容很危險,這段空間可能存放重要的系統(tǒng)數(shù)據(jù)或代碼
dos和其他合法程序一般都不會使用0:200~0:2ff的256字節(jié)空間,使用這段空間是安全的,謹慎起見我們還可以debug查看

6.包含多個段的程序
在代碼段中使用數(shù)據(jù)
之前提到的那一段安全的空間只有256字節(jié),我們需要超過256個字節(jié)的空間該怎么辦?在操作系統(tǒng)的環(huán)境中,合法地通過操作系統(tǒng)取得地空間都是安全的,因為操作系統(tǒng)不會讓一個程序所用的空間和其他程序以及系統(tǒng)自己的空間相沖突。
程序取得所需空間的兩種方法:
1.在加載程序的時候為程序分配
2.在程序執(zhí)行的過程中向系統(tǒng)申請
我們若要一個程序在被加載的時候取得所需的空間,則必須要在源程序中做出說明
當可執(zhí)行文件中的程序被加載入內存時,這些定義的數(shù)據(jù)同時也被加載入內存。與此同時,我們要處理的數(shù)據(jù)自然而然地獲得了存儲空間
dw 0123h,"dw"的含義是定義字形數(shù)據(jù)即define word
編程計算0123h、0456h、0789h、0abch、0defh、0cbah、0987h的和,結果存在ax中

assume cs:code

code segment
    dw 0123h,0456h,0789h,0abch,0defh,0cbah,0987h
  start:    mov bx,0
                mov ax,0
          
          mov cx,8
      s:    add ax,cs:[bx]
            add bx,2
          loop s
          
          mov ax,4c00h
          int 21h
code ends
end start

start這個標號在end后出現(xiàn)。偽指令end除了通知編譯器程序結束外,還可以通知編譯器程序的入口在什么地方。end指令指明了程序的入口在標號start處
偽指令end描述了程序的結束和程序的入口。在編譯、鏈接后,由"end start"指明的程序入口,被轉化為一個入口地址,存儲在可執(zhí)行文件的描述信息中
利用這種方法可以安排程序框架
assume cs:code
code segment
數(shù)據(jù)
start:
代碼
code ends
end start
在代碼段中使用棧
我們需要??臻g,當然也要由系統(tǒng)分配,正如上面定義數(shù)據(jù),數(shù)據(jù)就能載入內存。所以可以在程序中通過定義數(shù)據(jù)來取得一段空間,然后將這段空間作為??臻g使用
dw 0,0,0,0,0,0,0,0
之后合理設置棧頂ss:sp,這段空間就可以當作??臻g
所以描述dw的作用時,可以說用它定義數(shù)據(jù),也可以說用它開辟內存空間
將數(shù)據(jù)、代碼、棧放入不同空間
上述內容將他們放在一起程序顯得混亂,用到??臻g也小,代碼不長,放在一個段沒問題(8086模式一個段的容量不能大于64kb)
所以考慮用多個段存放數(shù)據(jù)、代碼

assume cs:code,ds:data,ss:stack
data segment
    dw 0123h,0456h,0789h,0abch,0defh,0cbah,0987h
data ends

stack segment
    dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
stack ends

code segment
start:  mov ax,stack
                mov ss,ax
        mov sp,20h
        
        mov ax,data
        mov ds,ax
        
        mov bx,0
        
        mov cx,8
    s:  push [bx]
            add bx,2
        loop s
        
        mov bx,0
        mov cx,8
        
   s0:  pop [bx]
            add bx,2
        loop s0
        
        mov ax,4c00h
        int 21h
code ends
end start

定義多個段的方法和定義一個段方法一樣
對段地址的引用:在程序中段名就相當于一個標號,它代表了段地址
我們定義了三個段,作用如同的名字含義,但是計算機不知道
我們只需設置start的位置在code段(cs:ip),ss:sp指向棧頂,ds指向data段,其他寄存器如bx存放data段中數(shù)據(jù)的偏移地址,即可按照我們的含義分段了

7.更靈活的定位內存地址的方法
and指令:邏輯與指令,按位進行與運算(對應位全1為1,不然為0)
例如指令
mov al,01100011B
and al, 00111011B
執(zhí)行后al = 00100011B
通過該指令可將操作對象的相應位設位0,其他位不變

or指令:邏輯或指令,按位進行或運算(對應位有1為1,全0為0)
例如指令
mov al,01100011B
or al, 00111011B
執(zhí)行后al = 01111011B
通過該指令可將操作對象的相應位設為1,其他位不變。

我們要把人能看懂的信息存儲在計算機中,就要對其及進行編碼,將其轉化為二進制信息進行存儲。計算機要將存儲的信息顯示出來看就需要對其進行解碼。
一個文本編輯過程中,就包含著按照ASCII編碼規(guī)則進行編碼和解碼:
鍵入"a"計算機用ASCII碼的規(guī)則對其進行編碼,將轉化為61H存儲在內存指定的空間中;文本編輯軟件從內存中取出61H,將其送到顯卡的顯存中;工作在文本模式下的顯卡,用ASCII碼的規(guī)則解釋顯存中的數(shù)據(jù),61H當作字符"a",顯卡驅動顯示器,將字符"a"圖像畫在屏幕上

匯編程序中'......'的方式指明數(shù)據(jù)是以字符的形式給出的,編譯器將其轉換對應ASCII碼
db 'unIX'相當于db 75H,6EH,49H,58H
大小寫轉換問題
首先分析:每個小寫字母的ASCII碼值比大寫字母ASCII碼值大20H,通過此方法可以將小寫字母轉化為大寫,可這要先判斷字母本身是否是大小寫,但目前水平沒有達到
再分析:字母ASCII碼的二進制,除第五位(從0開始計數(shù))外,大寫字母和小寫字母其他各位都一樣。大寫字母第五位為0,小寫字母第五位為1
因此可以用and 和 or指令將第五位置0或置1來改變大小寫

[bx+idata]
[bx]可以指明一個內存單元,[bx+idata]更靈活的指明內存單元,偏移地址(bx)+idata
段地址在ds中
db 'BaSiC'
我們要把這個字符串全部轉為大寫,可以把這個字符串看作一個數(shù)組,首地址就是B

SI和DI
si和di是8086CPU和bx功能相近的寄存器,si和di不能分成兩個8位寄存器來使用。
[bx+si]和[bx+di]
都表示一個內存單元,偏移地址是(bx)+(si),段地址在ds中
[bx+si+idata]和[bx+di+idata]
都表示一個內存單元,偏移地址位(bx)+(si)+idata,段地址在ds中
以上就是CPU提供的多種尋址方式
當我們需要匯編中的循環(huán)嵌套時,可以將外層循環(huán)的cx數(shù)值保存起來,在執(zhí)行外層循環(huán)loop指令前,再恢復外層循環(huán)cx數(shù)值。
一般來說,在需要暫存數(shù)據(jù)的時候,我們應該用棧
尋址方式的適當使用,是我們可以以更合理的結構來看待所要處理的數(shù)據(jù)。而為所要處理的看似雜亂的數(shù)據(jù)設計一種清晰的數(shù)據(jù)結構是程序設計的一個關鍵問題

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容