高速案例构造汇编器 + 快速加载 DPTR - 8051
high-speed case construct assembler + load DPTR fast - 8051
我目前正在为 8051 IC(特别是 AT89C4051)实现串行例程,我没有太多堆栈 space 或剩余内存,为了让我在串行端口(38K 或更高),我需要制作一个高速案例构造,因为在我的串行端口中断例程中,我正在构建一个数据包并检查其有效性。
假设我们在串行中断中,R0 是要接收数据的内存地址space。假设起始地址是 40h
所以我们来进行一些比较:
通过多次比较进行分支
serial:
mov A,SBUF
mov @R0,A
mov A,R0
anl A,#07h ;our packet is 8 bytes so get current packet # based on what we stored so far
cjne A,#0h,nCheckMe ;this gets scanned if A=7... waste 2 clock cycles
//We're working on first byte
ajmp theend
nCheckMe:
cjne A,#1h,nCheckThem ;this gets scanned if A=7... waste 2 clock cycles
//We're working on second byte
ajmp theend
nCheckThem:
...
cjne A,#7h,nCheckEnd
//We're working on last byte
ajmp theend
nCheckEnd:
theend:
inc R0
reti
上面的代码一开始可能是实用的,但随着要处理的数据包中当前字节的增加,由于额外的 "cjne" 指令处理,例程每次运行速度都会慢 2 个时钟周期。例如,如果我们在第 7 个字节上,那么 "cjne" 会发生很多次,因为它必须扫描每个案例,这会增加速度。
跳转分支
现在我想只使用一个跳转,但我不知道如何高速加载 DPTR,因为即使其他进程正在使用 DPTR 的值,中断也会被调用。
我想到了这段代码:
serial:
mov A,SBUF
mov @R0,A
mov A,R0
anl A,#07h ;our packet is 8 bytes so get current packet # based on what we stored so far
swap A ;multiply A times 16 and
rr A ;divide A by 2 so we get address times 8 since each block uses 8 bytes of code space.
mov R3,DPH ;save DPTR high byte without breaking stack
mov R6,DPL ;save DPTR low byte
mov dptr,#table
jmp @A+DPTR
theend:
mov DPL,R6 ;restore DPTR low byte
mov DPH,R3 ;restore DPTR high byte
inc R0 ;move on to next position
reti
table:
;insert 8 bytes worth of code for 1st case
;insert 8 bytes worth of code for 2nd case
;insert 8 bytes worth of code for 3rd case
...
;insert unlimited bytes worth of code for last case
在我的代码中,R3 和 R6 是空闲的,所以我用它们来存储旧的 DPTR 值,但是那些 mov 指令以及加载新的 DPTR 值每个需要 2 个周期,总共 10 个周期(包括恢复旧值) .
是否有更快的方法来处理 8051 汇编代码中的 case 构造,以便我的串行例程处理速度更快?
如果可能,不要运行 ISR 中的逻辑。如果您坚持,您可以将 DPTR 分配给 ISR,并且只在非常短的正常代码段中使用它,并禁用中断。或者,可以使用 PUSH+RET 技巧。
这是一种链式方法,其中每个处理的字符只是设置下一步的地址。如果您可以确保这些步骤在同一个 256 字节块内,则您只需要更新低字节。总开销为 8 个周期,但您还为算术节省了 4 个周期,因此它是 6 个周期的胜利。
.EQU PTR, 0x20 ; two bytes of SRAM
; Initialize PTR to address of step1
serial:
push PTR
push PTR+1
ret
step1:
; do stuff
mov PTR, #low8(step2)
reti
last_step:
; do stuff
mov PTR, #low8(step1)
reti
我目前正在为 8051 IC(特别是 AT89C4051)实现串行例程,我没有太多堆栈 space 或剩余内存,为了让我在串行端口(38K 或更高),我需要制作一个高速案例构造,因为在我的串行端口中断例程中,我正在构建一个数据包并检查其有效性。
假设我们在串行中断中,R0 是要接收数据的内存地址space。假设起始地址是 40h
所以我们来进行一些比较:
通过多次比较进行分支
serial:
mov A,SBUF
mov @R0,A
mov A,R0
anl A,#07h ;our packet is 8 bytes so get current packet # based on what we stored so far
cjne A,#0h,nCheckMe ;this gets scanned if A=7... waste 2 clock cycles
//We're working on first byte
ajmp theend
nCheckMe:
cjne A,#1h,nCheckThem ;this gets scanned if A=7... waste 2 clock cycles
//We're working on second byte
ajmp theend
nCheckThem:
...
cjne A,#7h,nCheckEnd
//We're working on last byte
ajmp theend
nCheckEnd:
theend:
inc R0
reti
上面的代码一开始可能是实用的,但随着要处理的数据包中当前字节的增加,由于额外的 "cjne" 指令处理,例程每次运行速度都会慢 2 个时钟周期。例如,如果我们在第 7 个字节上,那么 "cjne" 会发生很多次,因为它必须扫描每个案例,这会增加速度。
跳转分支
现在我想只使用一个跳转,但我不知道如何高速加载 DPTR,因为即使其他进程正在使用 DPTR 的值,中断也会被调用。
我想到了这段代码:
serial:
mov A,SBUF
mov @R0,A
mov A,R0
anl A,#07h ;our packet is 8 bytes so get current packet # based on what we stored so far
swap A ;multiply A times 16 and
rr A ;divide A by 2 so we get address times 8 since each block uses 8 bytes of code space.
mov R3,DPH ;save DPTR high byte without breaking stack
mov R6,DPL ;save DPTR low byte
mov dptr,#table
jmp @A+DPTR
theend:
mov DPL,R6 ;restore DPTR low byte
mov DPH,R3 ;restore DPTR high byte
inc R0 ;move on to next position
reti
table:
;insert 8 bytes worth of code for 1st case
;insert 8 bytes worth of code for 2nd case
;insert 8 bytes worth of code for 3rd case
...
;insert unlimited bytes worth of code for last case
在我的代码中,R3 和 R6 是空闲的,所以我用它们来存储旧的 DPTR 值,但是那些 mov 指令以及加载新的 DPTR 值每个需要 2 个周期,总共 10 个周期(包括恢复旧值) .
是否有更快的方法来处理 8051 汇编代码中的 case 构造,以便我的串行例程处理速度更快?
如果可能,不要运行 ISR 中的逻辑。如果您坚持,您可以将 DPTR 分配给 ISR,并且只在非常短的正常代码段中使用它,并禁用中断。或者,可以使用 PUSH+RET 技巧。
这是一种链式方法,其中每个处理的字符只是设置下一步的地址。如果您可以确保这些步骤在同一个 256 字节块内,则您只需要更新低字节。总开销为 8 个周期,但您还为算术节省了 4 个周期,因此它是 6 个周期的胜利。
.EQU PTR, 0x20 ; two bytes of SRAM
; Initialize PTR to address of step1
serial:
push PTR
push PTR+1
ret
step1:
; do stuff
mov PTR, #low8(step2)
reti
last_step:
; do stuff
mov PTR, #low8(step1)
reti