注意:本文经过原作者授权转译,转载请标明出处
原文地址: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