注意:本文經過原作者授權轉譯,轉載請標明出處
原文地址:http://mrjester.hapisan.com/04_MC68/Sect03Part05/Index.html
條件允許建議閱讀原文,網上非中文資料還是較多,當作鍛煉英文豈不美哉
翻譯若有不足之處歡迎批評指正
譯文:
"好朋友只能由偶然相遇的天性和諧的雙方組成,並不是想成為好朋友就能成為好朋友" ---- 佛
簡介
直到第三章的目前為止,我們已經學習了一系列以字節
,字
和長字
為單位的對二進制數據的修改指令。那么如果你想要修改數據中的某一位
而不是好幾位
該怎么辦呢?
接下來的三個指令就是為這個疑問而設計的,請留意它們有着非常相似的使用規則,所以它們在匯編的時候是歸於一類的
BSET 指令
BSET - 設置某個位為1
這條指令會把目的操作數
中的某一位設置成1
,具體是哪一位取決於源操作數
例子
匯編程序可能會對這些指令的使用比較挑剔,讓我們先來康康一個在數據寄存器上使用這條指令的例子:
bset.l #$0E, d0
我們知道數據寄存器d0
中是一個長字
的內容 (32 位
):
1F | 1E | 1D | 1C | 1B | 1A | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 0F | 0E | 0D | 0C | 0B | 0A | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
如你所見,在指令執行前,數據寄存器d0
中的32位
全是0
,上面的每個數字表示每一位
在上面的例子里,源操作數
是0E
,所以d0
的0E
位會被設置成1
:
1F | 1E | 1D | 1C | 1B | 1A | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 0F | 0E | 0D | 0C | 0B | 0A | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
此時d0
的內容是00004000
(二進制表示為:0000 0000 0000 0000 0100 0000 0000 0000
)
差不多就是這么簡單,不過我們再康一個例子加深下理解:(假設d0
的內容現在還是00004000
)
bset.l #$01, d0
這條指令會把d0
的第01
位
設置為1
:
1F | 1E | 1D | 1C | 1B | 1A | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 0F | 0E | 0D | 0C | 0B | 0A | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
現在d0
的內容變成了00004002
(二進制表示為:0000 0000 0000 0000 0100 0000 0000 0010
)
當然了,源操作數
的內容可以是一個大於1F
的值,那么此時指令會怎么執行呢?比如:
bset.l #$27, d0
由於最高位是第1F位
,所以任何更高的位都會從00
重新計算,比如20
會變成00
,21
會變成01
,22
會變成02
等等,所以對於上面這條指令:
1F | 1E | 1D | 1C | 1B | 1A | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 0F | 0E | 0D | 0C | 0B | 0A | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
第07位
被設置為1
,任何更高位都會重新計算 (事實上,這條指令只讀取了源操作數
的后5位
,因為二進制中5位可以表示00
-1F
)
注意 如果
目的操作數
是一個數據寄存器 (就像上面例子列出的那樣),那么長度必須是長字
(.l
),你不能對數據寄存器使用字節
或是字
長度
現在讓我們再康康這條指令用在內存上的例子:
bset.b #$04, $000009C8
先看看指令執行前內存的內容:
偏移量 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
... | ||||||||||||||||
000009A0 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 43 | 55 | 4E | 54 |
000009B0 | 00 | 00 | FE | DC | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
000009C0 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 03 | 40 | 3F | 00 | 00 | 00 | 00 | 00 | 00 |
000009D0 | 10 | 20 | 79 | 2A | B2 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
... |
內存中地址為$000009C8
的內容是40
,換算成二進制就是0100 0000
:
07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 | |
---|---|---|---|---|---|---|---|---|
40 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
因為源操作數
是04
,所以把第04位
設為1
:
07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 | |
---|---|---|---|---|---|---|---|---|
50 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 |
結果變成了50
,然后存入到內存中去:
偏移量 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
... | ||||||||||||||||
000009A0 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 43 | 55 | 4E | 54 |
000009B0 | 00 | 00 | FE | DC | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
000009C0 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 03 | 50 | 3F | 00 | 00 | 00 | 00 | 00 | 00 |
000009D0 | 10 | 20 | 79 | 2A | B2 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
... |
同樣的對於內存的操作,如果源操作數
大於07
,它將會從00
開始重新算,08
變成00
,09
變成01
,0A
變成02
等等
注意 如果
目的操作數
是一個內存地址,或是通過地址寄存器獲得的內存地址,那么長度必須是字節
(.b
),你不能對內存地址使用字
或是長字
長度
同時源操作數
可以是立即數:
bset.l #$00, d0
也可以是數據寄存器:
bset.l d2, d0
它不能是一個內存地址,或是通過地址寄存器獲得的內存地址,或是直接一個地址寄存器,比如下面這些錯誤的示范:
bset.l a0, d0 ✖
bset.l (a0), d0 ✖
bset.l $20(a0), d0 ✖
bset.l (a0)+, d0 ✖
bset.l -(a0), d0 ✖
bset.l $00FF8010, d0 ✖
而目的操作數
可以是數據寄存器或是內存地址 ,或是通過地址寄存器獲得的內存地址:
bset.l #$00, d0
bset.b #$00, $00FF8010
bset.b #$00, (a0)
bset.b #$00, $20(a0)
bset.b #$00, (a0)+
bset.b #$00, -(a0)
但是不能是一個地址寄存器:
bset.l #$00, a0 ✖
接下來你會發現另兩個指令BCLR
和BCHG
也有着相同的規則。
BCLR 指令
BCLR - 擦除某個位為0
這條指令會把目的操作數
中的某一位擦除成0
,具體是哪一位取決於源操作數
例子
和bset
指令幾乎相同,除了僅僅一點:
bclr.l #$0E, d0
1F | 1E | 1D | 1C | 1B | 1A | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 0F | 0E | 0D | 0C | 0B | 0A | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
它會把那一位
擦除成0
而不是設置為1
,除此之外無任何區別,我想在這里就不贅述了
BCHG 指令
BCHG - 翻轉某個位
這條指令會把目的操作數
中的某一位做翻轉,如果是0
就會變成1
,如果是1
就會變成0
,具體是哪一位取決於源操作數
例子
同樣的規則,除了一點,比如d0
原本內容是480E072C
:
1F | 1E | 1D | 1C | 1B | 1A | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 0F | 0E | 0D | 0C | 0B | 0A | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 0 |
然后執行完下面這條指令后:
bchg.l #$07, d0
因為d0
中第07
位
原本是0
,所以執行后會變成1
:
1F | 1E | 1D | 1C | 1B | 1A | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 0F | 0E | 0D | 0C | 0B | 0A | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 |
如果此時再執行下面這條指令:
bchg.l #$1B, d0
因為d0
中第1B
位
原本是1
,所以執行后會變成0
:
1F | 1E | 1D | 1C | 1B | 1A | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 0F | 0E | 0D | 0C | 0B | 0A | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 |
所以現在d0
的內容是400E07AC
如你所見,上面原本是0
(擦除) 的位
被修改成了1
,而原本是1
(設置) 的位
被修改成了0
,就是這么簡單
除了這些,別的規則和bset
與bclr
完全一樣
家庭作業
現在我們已經學習了主要的位指令,讓我們來康一個測試吧:
move.b #$24, d0
bclr.l #$02, d0
ori.b #$03, d0
move.b #$F8, d1
and.b d0, d1
not.w d1
eori.w #$FF00, d1
move.b d1, $00002200
bset.b #$05, $00002200
bchg.b #$01, $00002200
所有寄存器的初始值都是00000000
,你的任務就是算出內存中地址為00002200
處的字節
是什么,像往常一樣,答案在下一節,看之前先自己捋一遍哦
目錄
上一篇:[轉譯][馬基 傑斯特(MarkeyJester) 摩托羅拉68000 入門教程] 叄 - 位 指令 | 4. EOR 指令
下一篇:[轉譯][馬基 傑斯特(MarkeyJester) 摩托羅拉68000 入門教程] 叄 - 位 指令 | 6. 家庭作業答案 - 3