📄 emc单片机编程技巧集锦.txt
字号:
OR A,INT24+1
OR A,INT24+2
JBS PSW,FZ
;...
复制某些位元
有时候我们需要将一些特定的几个位元由某个暂存器复制给另一组暂存器,由于并非完全复制暂存器的内容,所以会多了一些抽取位元的步骤,现在我们找到一个方法,只要四个步骤就可以将指定的位元复制到另一组暂存器里面,举例说明,假设位元复制前(SOURCE)=44H,(TARGET)=5AH,如果我们希望将SOURCE的BIT0~BIT2复制到TARGET,则执行程式后(SOURCE)=44H,(TARGET)=5CH。
MOV A,SOURCE
XOR A,TARGET
AND A,@00000111B
XOR TARGET,A
无论您希望复制哪几个BIT,只要将第三行程式MASK所需的位元即可。
奇偶位元对调
以下这段程式是根据Dmitry
Kiryashov的演算法设计,假设原本ACC内所有位元的排列顺序为abcdefgh,交换后ACC顺序变成badcfehg,程式只有五行,颇耐人寻味。
MOV REG,A
AND A,@0x55
ADD REG,A
RRC REG
ADD A,REG
中断程式不需保留ACC及PSW的方法
中断程式一定要保留ACC及PSW吗?那倒未必!特别是如果您使用的是EM78P152/156之类的迷你级的MCU,RAM
SIZE都特别小,如果您只需要让TCC中断做简单的计数工作,只要小心使用指令,就可以避免中断程式会破坏到ACC及PSW。原因是有些指令并不会对PSW产生影响,有些指令不需要经过ACC。首先设定好预除器,并且让TCC
Free Run。下面的例子完全没用到ACC及PSW。
ORG 0
JMP INIT
ORG 8
TCCINT:
BC RF,TCIF ;清除中断旗标
INC COUNTER
RETI
Multiple Task管理与状态机
Multiple
Task就是将CPU时间平均分配(也可以是不平均分配)给多个Task,所以在程式中会有一个时间管理者,依照指定的时间对指定的Task服务,没有分配到时间的Task必需等候时间到来才能执行。
TCCINT:
MOV R10,A
SWAP R10
SWAPA PSW
MOV R11,A
INC TASK
MOV A,@4
SUB A,TASK
JBS PSW,FC
JMP ENDINT
CLR TASK
ENDINT:
BC ISR,TCIF
SWAPA R11
MOV PSW,A
SWAPA R10
RETI
;-------------------------
MAIN:
MOV A,@0x21
CONTW
CLR TCC
CLR ISR
MOV A,@0x01
IOW IOCF
CLR TASK
START:
MOV A,TASK
TBL
JMP TASK0
JMP TASK1
JMP TASK2
JMP TASK3
JMP TASK4
;-------------------------
TASK0:
; ....
JMP START
TASK1:
; ....
JMP START
TASK2:
; ....
JMP START
TASK3:
; ....
JMP START
TASK4:
; ....
JMP START
上面这个程式将TCC规划为62.5ms中断一次(系统震荡选用32.768KHz),所以Task每62.5ms会切换到下一个Task,也就是说每个Task都能够平均分享CPU的时间,这就是分时多工的原理。至于中断程式部分不是必须的,可一情况决定是否要由TCC安排时间的管理。状态机(State
Machine)是根据目前所在的State所产生的条件,来决定下一个状态,所以程式原理和上面这个例子大同小异,所不同的是,我们应该把标示为TASKn的Label视为一个单独的State,然后根据某些条件将最后面的JMP转移到另外一个State。在这里时间控制也不一定要用到,视需求决定。例如:
TASK1:
MOV A,INPUT
JBS PSW,FZ
JMP TASK2
JMP TASK3
说明
如果INPUT=0的话,将由目前所在的TASK1转移到TASK3执行,否则状态转移到TASK2。
后记
戏法人人会变,只是巧妙各有不同,希望笔者提供的这些小技巧对于喜欢玩单晶片的读者能够有所助益,我们不仅只是强调硬体应该节省,在软体技巧上也应该多发展一些好的演算法,如此才能双管齐下,对症下药。吾人期盼借此抛砖引玉能激发您更多的创意,写出更精简的程式,也期盼您的指教。
竭诚欢迎所有喜爱EM78x系列单晶片的朋友来信和我们一起讨论。
笔者E-MAIL:sa2tjw@emc.com.tw
pheavecn赏析1:
引:
ACC与暂存器内容交换
这理我们要介绍一种快速的逻辑演算法,只需要3个指令CYCLE,就可以将ACC的内容与暂存器的内容交换,不拖泥带水,Very cute!
XOR Number,A
XOR A,Number
XOR Number,A
======================================
析:
这个技巧是所有单片机都通用的。也是我们最多机会用到的。
大多数场合的做法是:
MOV Temp1,A
MOV A,Number
MOV Temp2,A
MOV A,Temp1
MOV Number,A
MOV a,Temp2
用了2个过程变量,6个指令周期。
XOR指令实际上是按位做二进制加法,进位丢弃。
称Number的0位(bit0)为Bn,A的0位为Ba;
算法的分析如下:
1、XOR Number,A ;Bn+Ba==>Number的0位
2、XOR A,Number
;Bn+Ba+Ba==>ACC的0位,由于Ba+Ba必等于0,所以现在ACC的0位就等于Bn,即原来Number的0位。
3、XOR Number,A
;Bn+Ba+Bn==>Number的0位,同上理,Bn+Bn必等于0,现在Number的0位等于原来ACC的0位。
必须注意,XOR指令影响Z标志位。
HOLTEK芯片的实现指令为:
XORM A,Number
XOR A,Number
XORM A,Number
同样只影响Z标志位。
pheavecn赏析2:
引:
节省NOP指令的方法
您还在为程式挤不下伤脑筋吗? NOP指令有时候在延迟指令时间很有用,假如你有连续两个NOP指令可以用JMP到下一个指令的方式代替,因为这样可以减少一个指令BYTE,又可以达到相同的效果。
例如:
NOP
NOP
可以写成:
JMP NEXT_INST
NEXT_INST:
;....
因为一个NOP花费一个指令Cycle,但是一个JMP指令就需要2个指令Cycle,虽然有时候会抱怨JMP指令会多花一点时间,但是想不到它也有如此妙用吧。
=========================================
析:
我的做法是定义一个预处理宏:
#define nop2 JMP $+1
需要延时2个指令周期时,用NOP2指令就行了。
对EMC、HOLTEK都适用,但是对NTK4位单片机无效,因为NTK所有指令都是单周期的。
注:$表示指令所在PC地址
pheavecn赏析3:
引:
交换两组暂存器的内容
如果你觉得要交换两组记忆体的内容一定要借用第三组变数,那么您可以参考以下的方式,只是用了一些数学技巧就变得又快又简单。
MOV A,REG1
SUB A,REG2
ADD REG1,A
SUB REG2,A
原理说明
A=REG1
A=REG2-REG1
REG1=REG1+A
=REG1+(REG2-REG1)
=REG2
REG2=REG2-(REG2-REG1)
=REG1
若X>Y就交换...
延续上一个例子,此法用应用在Bubble Sort特别管用。
MOV A,X
SUB A,Y
JBC PSW,FC
JMP NO_CHANGE
ADD X,A
SUB Y,A
==================================
析:
这个技巧相对来说使用频率比较少,而且通用性比较差。
对HOLTEK芯片需要更多指令周期:
mov a,Reg1
sub a,Reg2 ;a=reg1-reg2(与EMC指令不同)
addm a,Reg2 ;reg2=reg2+reg1-reg2=reg1(先实现从reg1到reg2)
subm a,Reg1 ;reg1=a-reg1=(reg1-reg2)-reg1=-reg2
cpl reg1
inc reg1 ;这两句实现reg1=-(-reg2)=reg2
由于减法指令的不同,HOLTEK需要6条指令。
pheavecn赏析4:
引:
LABEL太多?
写组合语言最令人伤脑筋的问题之一就是程式中到处是label,这有两个坏处,第一就是不小心就会造成label重复的问题,第二就是想不出适当的label名称。如果您已经为label的命名问题肠枯思竭,给您提供一个小方法,程式中如果用「$」可以表示目前PC的位址,依此推论「$+2」表示PC+2,「$-4」表示PC-4,看看底下的例子您立刻就明白:
MOV R,R
JBS PSW,FZ
JMP $+2
JMP $-4
; ....
不过也要给您一个建议,label有个重要的意义就是具有注解的功能,特别是针对一些懒的写注解的人格外重要。所以这个方法仅适合使用在重复性很高的程式片断。
================================
析:
这个问题对汇编程序的可读性影响很大。有经验的汇编程序员都应该很好的应用这个符号。掌握使用$的尺度,需要一段时间。
我的原则是:1、必须是RISC单字指令的指令系统,对51系列不适用。
2、跳转是局部算法内跳转,即本小段程序在整体程序中可以视作不可分割。
3、小段程序不超过一屏,即在屏幕上可以看到整段程序。
pheavecn赏析5:
引:
计算一个BYTE中有多少个"1"
这个小程式可以检查出在某个BYTE中共有几个1,在某些演算法的过程可能会用得到,计算的结果放在ACC。
RRCA DATA
AND A,@0x55
SUB DATA,A
MOV A,DATA
AND A,@0x33
ADD DATA,A
RRC DATA
ADD DATA,A
RRC DATA
SWAPA DATA
ADD A,DATA
AND A,@0x0F
===================
析:
计算一个字节有多少个1,我想不出有什么用处,但是可以做通讯时的奇偶校验。
用HOLTEK语言要改成下面的写法:
mov a,data
and a,55h
adcm a,data
rrc data
mov a,data
and a,33h
addm a,data
rr data
addm a,data
rr data
swapa data
add a,data
and a,0fh
结束时acc中即是原DATA中1的个数。
如果只是奇偶校验,应该还可以简化.
high关于$:
$还可以解决宏的复用.
当宏里使用到LABLE时,这个宏就只能用一次.
想让宏多次使用,解决办法就是用$.
程序匠人:另外商量个事:用JMP $+1代替NOP,要注意跨页时别跳错了。
pheavecn:
在页界我都用ORG 7FFH等再加NOP,严格规定不可程序跨页。
不过我定义的NOP2确实有隐患在里头。匠人眼睛真厉害。
请问匠人一般如何处理程序跨页问题?
程序匠人:
我一般也是在页界作处理,不让程序漫出来
我在页末加拦截程序(软件陷阱),既可防止程序编写时过界,又可拦截程序的跳飞。
NOP
NOP
JMP ERR_CNT
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -