3

I have received a character over UART, and need to validate if it's a number character.

Normally I'd do

if (char >= '0' && char <= '9') { /* VALID */ }

However, I have to do it in assembly.

I haven't found any compare instruction (so I assume there is none).

How can I do this?

    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:

edit: ok here's what I have now but it doesn't work

;======================================================
; 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
;======================================================
MightyPork
  • 18,270
  • 10
  • 79
  • 133
  • 2
    You can use `SUBB` or `ADD` instead in conjunction with `JC`/`JNC`. Is that enough hint? – Jester Apr 08 '15 at 16:10
  • Yeah I see the idea of using carry and then JC and JNC, I'm just not really sure how to do it. – MightyPork Apr 08 '15 at 16:12
  • 1
    You the addition or subtraction operation to potentially set the flag, then the conditional jump to act on it. – Chris Stratton Apr 08 '15 at 16:15
  • For example, you can subtract `'0'` and if you got a carry that means the value was less, so you should jump to invalid case. Similarly, subtract `'9'+1` and if that does not produce carry, that means the value was bigger than `9` so again invalid. – Jester Apr 08 '15 at 16:15
  • @Jester could you please check my code that i just added? I tried ti implement it but it won't work – MightyPork Apr 08 '15 at 17:03
  • If `A` has the value `'5'`, then what will the state of the carry bit be when you do, `SUBB A, #'9'+1`? – lurker Apr 08 '15 at 18:18
  • Yeah I messed up there, it's working now – MightyPork Apr 08 '15 at 18:19

2 Answers2

3

Using this technique, if (a >= '0' && a <= '9') can be transformed into

if ((unsigned char)(a - '0') <= ('9'-'0'))

which saves you a comparison and a jump. Now only a single comparison is enough

The result might be like this

    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:

Most modern compilers know how to optimize this range check. I can't find a 8051 online compiler nor do I have an offline compiler for it, but AVR would be close enough to give a demo. AVR gcc gives the same output for 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

Update:

Sample output for 8051 from 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
phuclv
  • 37,963
  • 15
  • 156
  • 475
1

The easiest thing to do is compile it in C and check the listing file. Here is what my compiler produces.

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:
ACRL
  • 1,820
  • 1
  • 13
  • 17
  • 1
    but that's not the most optimized way and it needs 2 checks + 2 jumps – phuclv Jun 29 '15 at 05:27
  • 1
    Get a better compiler if it doesn't optimize range-checks for you. GCC and clang use the technique that phuclv's answer shows, at least when compiling for mainstream CPUs. (I don't know if either one can target 8051). – Peter Cordes May 25 '20 at 13:40