检查数字是否在 8051 的范围内
Check if number is in range on 8051
我通过 UART 接收到一个字符,需要验证它是否是数字字符。
通常我会这样做
if (char >= '0' && char <= '9') { /* VALID */ }
但是,我必须在汇编中完成。
我没有找到任何比较指令(所以我假设有none)。
我该怎么做?
mov A, SBUF ; load the number
; -- pseudocode --
cmp A, #'0' ; In AVR, I'd do it this way
brlt fail ; but I'm new to 8051
cmp A, #'9'
brge fail
; -- pseudocode --
; number is good
fail:
编辑:好的,这就是我现在拥有的,但它不起作用
;======================================================
; check if r1 is number char -> r0 = 1 or 0
;------------------------------------------------------
fn_isnum:
PUSH acc
PUSH 1
MOV r1,A
SUBB A,#'0'
JC isnum_bad
MOV A,r1
SUBB A,#'9'+1
JC isnum_bad
POP 1
POP acc
MOV r0,#1
RET
isnum_bad:
POP 1
POP acc
MOV r0,#0
RET
;======================================================
利用this technique,if (a >= '0' && a <= '9')
可以转化为
if ((unsigned char)(a - '0') <= ('9'-'0'))
这样就省去了比较和跳转。现在只需比较一次就够了
结果可能是这样
SUBB A, #'0' ; A = a - '0'
CLR C
MOV R1, A ; R1 = a - '0'
MOV A, #9 ; A = '9' - '0'
SUBB A, R1 ; C = 1 if ('9' - '0') < (a - '0')
JC bad: ; jump when C != 0, i.e. !((a - '0') <= ('9' - '0'))
valid:
; do something
bad:
大多数现代编译器都知道如何优化此范围检查。我找不到 8051 在线编译器,也没有适用于它的离线编译器,但 AVR 足以提供演示。 AVR gcc 为 both the original condition and the transformed one
提供相同的输出
mov r25,r24 ; input in r24
subi r25,lo8(-(-48)) ; r25 = input - '0'
ldi r24,lo8(1) ; r24 = 1
cpi r25,lo8(10) ; if r25 < 10
brlo .L2 ; jump to .L2
ldi r24,lo8(0) ; r24 = 0
.L2:
ret ; return value in r24
更新:
Sample output for 8051 来自 SDCC
mov r7,dpl
cjne r7,#0x30,00110$
00110$:
jc 00103$
mov a,r7
add a,#0xff - 0x39
jnc 00104$
00103$:
mov r7,#0x00
sjmp 00105$
00104$:
mov r7,#0x01
00105$:
mov dpl,r7
ret
最简单的做法是用C 编译它并检查列表文件。这是我的编译器生成的结果。
MOV A, SBUF
CLR C
SUBB A, #030H
JC ?C0026
MOV A, SBUF
SETB C
SUBB A, #039H
JNC ?C0026
; Put instructions here to execute when the code is valid
?C0026:
我通过 UART 接收到一个字符,需要验证它是否是数字字符。
通常我会这样做
if (char >= '0' && char <= '9') { /* VALID */ }
但是,我必须在汇编中完成。
我没有找到任何比较指令(所以我假设有none)。
我该怎么做?
mov A, SBUF ; load the number
; -- pseudocode --
cmp A, #'0' ; In AVR, I'd do it this way
brlt fail ; but I'm new to 8051
cmp A, #'9'
brge fail
; -- pseudocode --
; number is good
fail:
编辑:好的,这就是我现在拥有的,但它不起作用
;======================================================
; check if r1 is number char -> r0 = 1 or 0
;------------------------------------------------------
fn_isnum:
PUSH acc
PUSH 1
MOV r1,A
SUBB A,#'0'
JC isnum_bad
MOV A,r1
SUBB A,#'9'+1
JC isnum_bad
POP 1
POP acc
MOV r0,#1
RET
isnum_bad:
POP 1
POP acc
MOV r0,#0
RET
;======================================================
利用this technique,if (a >= '0' && a <= '9')
可以转化为
if ((unsigned char)(a - '0') <= ('9'-'0'))
这样就省去了比较和跳转。现在只需比较一次就够了
结果可能是这样
SUBB A, #'0' ; A = a - '0'
CLR C
MOV R1, A ; R1 = a - '0'
MOV A, #9 ; A = '9' - '0'
SUBB A, R1 ; C = 1 if ('9' - '0') < (a - '0')
JC bad: ; jump when C != 0, i.e. !((a - '0') <= ('9' - '0'))
valid:
; do something
bad:
大多数现代编译器都知道如何优化此范围检查。我找不到 8051 在线编译器,也没有适用于它的离线编译器,但 AVR 足以提供演示。 AVR gcc 为 both the original condition and the transformed one
提供相同的输出 mov r25,r24 ; input in r24
subi r25,lo8(-(-48)) ; r25 = input - '0'
ldi r24,lo8(1) ; r24 = 1
cpi r25,lo8(10) ; if r25 < 10
brlo .L2 ; jump to .L2
ldi r24,lo8(0) ; r24 = 0
.L2:
ret ; return value in r24
更新:
Sample output for 8051 来自 SDCC
mov r7,dpl
cjne r7,#0x30,00110$
00110$:
jc 00103$
mov a,r7
add a,#0xff - 0x39
jnc 00104$
00103$:
mov r7,#0x00
sjmp 00105$
00104$:
mov r7,#0x01
00105$:
mov dpl,r7
ret
最简单的做法是用C 编译它并检查列表文件。这是我的编译器生成的结果。
MOV A, SBUF
CLR C
SUBB A, #030H
JC ?C0026
MOV A, SBUF
SETB C
SUBB A, #039H
JNC ?C0026
; Put instructions here to execute when the code is valid
?C0026: