Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

C to ASM translation

Status
Not open for further replies.

twins

New Member
I'm trying to make an rpm meter to output to LCD display with this code in C but am having problem changing it to assembly language as there are no division in the instruction set for P18F4580. Can anyone help?

RPM[0] = num/10000 + 48;
RPM[1] = (num/1000)%10 + 48;
RPM[2] = (num/100)%10 + 48;
RPM[3] = (num/10)%10 + 48;
RPM[4] = num%10 + 48;
 
Then write, or download, suitable division routines.

I don't use C myself, but the 18F is intended more than C than assembler, to the extent there's a free C compiler you can download and most application notes are in C, so why not use C?.
 
I'm trying to make an rpm meter to output to LCD display with this code in C but am having problem changing it to assembly language as there are no division in the instruction set for P18F4580. Can anyone help?

RPM[0] = num/10000 + 48;
RPM[1] = (num/1000)%10 + 48;
RPM[2] = (num/100)%10 + 48;
RPM[3] = (num/10)%10 + 48;
RPM[4] = num%10 + 48;

hi,
I use this subr, just change the Ascbfr to RPM
Code:
;16 Bit Binary to 5 Ascii Routine for 18F1320   24May08  
;tested works OK.

    list    p=18f1320,f=inhX32
    #include <p18f1320.inc>

    errorlevel -302, -207

    config    OSC=INTIO2,PWRT=ON, LVP=OFF,BOR=OFF, WDT=OFF,DEBUG=OFF
    config    MCLRE =OFF

;AscBfrs for convert subr  
    AscBfr4    equ 0x020
    AscBfr3    equ 0x021
    AscBfr2    equ 0x022
    AscBfr1    equ 0x023
    AscBfr0    equ 0x024
         
    BinValL    equ 0x028
    BinValM    equ 0x029

    No_Bits    equ 0x02B
    Digit_Cnt equ 0x02C
         

    org    0x0000
    goto    Main        ;go to start of main code

    org    0x0008        ;High priority interrupt vector
    bra    HighInt        ;go to high priority interrupt routine
    org    0x0018        ;low priority interrupts
    retfie
HighInt: 
    retfie    FAST

Main:
    movlw    39h        ;testing preload value #12345
    movwf    BinValL
    movlw    30h
    movwf    BinValM

    call    Bin2Asc
loop:
    goto    loop

;Binary to Ascii conversion Subr.........................  
;Originally written for 16F series PIC's, Author Unknown.  
;Conversion to 18F series PIC by esp1  
;18F1320 convert 16bit bin to 5 asci in ASCBFR0  
;enter with BinValH and BinValL holding the 16bit binary value to be converted.  
;exit with  5 Ascii value,,,, the BinValH/L is all '0' on exit  

Bin2Asc: 
    clrf    AscBfr4        ;clear buffers
    clrf    AscBfr3
    clrf    AscBfr2
    clrf    AscBfr1
    clrf    AscBfr0
    movlw    .16
    movwf    No_Bits
BitLoop: 
    rlcf    BinValL,F 
    rlcf    BinValM,F    ;carry = msb < 1

    LFSR    FSR0,AscBfr0    ;lsd 1st
    movwf    FSR0        ;pointer to ascbfr 
    movlw    0X05        ;digits to do
    movwf    Digit_Cnt
AdjLoop: 
    rlcf    INDF0,F        ;shift ASCII byte in [INDF0] 1 bit left
    movlw    0X0A        ;decimal 10
    subwf    INDF0,W        ;W=[INDF0]-10

    btfsc    STATUS,C    ;skip if no carry,  its a negative result
    movwf    INDF0        ;only save if postive result  

    movf    STATUS,W    ;NB. the decf FSR0L sets Carry!
    decf    FSR0L,F        ;point to next msd Ascbfr  
    movwf    STATUS        ;recall Status from before the decf FSR0L

    decfsz    Digit_Cnt,F    ;dec AscBfr counter
    goto    AdjLoop        ;go next ascii

    decfsz    No_Bits,F
    goto    BitLoop        ;go next bit shift left for BinVal

    movlw    0X30        ;make asci
    iorwf    AscBfr4,F
    iorwf    AscBfr3,F
    iorwf    AscBfr2,F
    iorwf    AscBfr1,F
    iorwf    AscBfr0,F
    return

    end
 
Then write, or download, suitable division routines.

I don't use C myself, but the 18F is intended more than C than assembler, to the extent there's a free C compiler you can download and most application notes are in C, so why not use C?.

I just started learning PIC 3 mths ago and it was in assembly language. So for now I'm unfamiliar with the C language when programming a PIC but will definitely learn it for the times to come.

hi,
I use this subr, just change the Ascbfr to RPM
Code:
;16 Bit Binary to 5 Ascii Routine for 18F1320   24May08  
;tested works OK.

    list    p=18f1320,f=inhX32
    #include <p18f1320.inc>

    errorlevel -302, -207

    config    OSC=INTIO2,PWRT=ON, LVP=OFF,BOR=OFF, WDT=OFF,DEBUG=OFF
    config    MCLRE =OFF

;AscBfrs for convert subr  
    AscBfr4    equ 0x020
    AscBfr3    equ 0x021
    AscBfr2    equ 0x022
    AscBfr1    equ 0x023
    AscBfr0    equ 0x024
         
    BinValL    equ 0x028
    BinValM    equ 0x029

    No_Bits    equ 0x02B
    Digit_Cnt equ 0x02C
         

    org    0x0000
    goto    Main        ;go to start of main code

    org    0x0008        ;High priority interrupt vector
    bra    HighInt        ;go to high priority interrupt routine
    org    0x0018        ;low priority interrupts
    retfie
HighInt: 
    retfie    FAST

Main:
    movlw    39h        ;testing preload value #12345
    movwf    BinValL
    movlw    30h
    movwf    BinValM

    call    Bin2Asc
loop:
    goto    loop

;Binary to Ascii conversion Subr.........................  
;Originally written for 16F series PIC's, Author Unknown.  
;Conversion to 18F series PIC by esp1  
;18F1320 convert 16bit bin to 5 asci in ASCBFR0  
;enter with BinValH and BinValL holding the 16bit binary value to be converted.  
;exit with  5 Ascii value,,,, the BinValH/L is all '0' on exit  

Bin2Asc: 
    clrf    AscBfr4        ;clear buffers
    clrf    AscBfr3
    clrf    AscBfr2
    clrf    AscBfr1
    clrf    AscBfr0
    movlw    .16
    movwf    No_Bits
BitLoop: 
    rlcf    BinValL,F 
    rlcf    BinValM,F    ;carry = msb < 1

    LFSR    FSR0,AscBfr0    ;lsd 1st
    movwf    FSR0        ;pointer to ascbfr 
    movlw    0X05        ;digits to do
    movwf    Digit_Cnt
AdjLoop: 
    rlcf    INDF0,F        ;shift ASCII byte in [INDF0] 1 bit left
    movlw    0X0A        ;decimal 10
    subwf    INDF0,W        ;W=[INDF0]-10

    btfsc    STATUS,C    ;skip if no carry,  its a negative result
    movwf    INDF0        ;only save if postive result  

    movf    STATUS,W    ;NB. the decf FSR0L sets Carry!
    decf    FSR0L,F        ;point to next msd Ascbfr  
    movwf    STATUS        ;recall Status from before the decf FSR0L

    decfsz    Digit_Cnt,F    ;dec AscBfr counter
    goto    AdjLoop        ;go next ascii

    decfsz    No_Bits,F
    goto    BitLoop        ;go next bit shift left for BinVal

    movlw    0X30        ;make asci
    iorwf    AscBfr4,F
    iorwf    AscBfr3,F
    iorwf    AscBfr2,F
    iorwf    AscBfr1,F
    iorwf    AscBfr0,F
    return

    end

Thank you ericgibbs. Will try to understand the codes as I type it in my assembler. ^_^
 
What you are looking for is not really a divide. It is a BCD conversion. The "48" is the ASCII code for the character "0" so the results are "0" to "9" as characters.

This is code that I use. It only works up to 4096, I think. However you should be able to see how it works and it could be extended to larger numbers.

Code:
bcdconvert
;This converts hex1:hex0 to bcd. The top half of hex1 must be zero
;the result is in bcd1:bcd0
;The maximum output is 4096 

	clrf    bcd1

	movlw	0x0f
	subwf 	hex1, w
	bnz	comp_4000
	movlw	0xa0
	subwf 	hex0, w
comp_4000
	bnc	no_4000	
sub_4000
	bsf	bcd1, 6
	movlw	0xa0
	subwf 	hex0, f
	movlw	0x0f
	subwfb	hex1, f
	
no_4000	
	movlw	0x07
	subwf 	hex1, w
	bnz	comp_2000
	movlw	0xd0
	subwf 	hex0, w
comp_2000
	bnc	no_2000	
sub_2000
	bsf	bcd1, 5
	movlw	0xd0
	subwf 	hex0, f
	movlw	0x07
	subwfb	hex1, f

no_2000	
	movlw	0x03
	subwf 	hex1, w
	bnc	no_800			;as 800 and 1000 share 3 as the first hex digit
	bnz	comp_1000
	movlw	0xe8
	subwf 	hex0, w
comp_1000
	bnc	no_1000	
sub_1000
	bsf	bcd1, 4
	movlw	0xe8
	subwf 	hex0, f
	movlw	0x03
	subwfb	hex1, f

no_1000
bcd_convert_1000
	movlw	0x03
	subwf 	hex1, w
	bnz	comp_800
	movlw	0x20
	subwf 	hex0, w	
comp_800
	bnc	no_800	
sub_800
	bsf	bcd1, 3
	movlw	0x20
	subwf 	hex0, f
	movlw	0x03
	subwfb	hex1, f

no_800
	movlw	0x01
	subwf 	hex1, w
	bnz	comp_400
	movlw	0x90
	subwf 	hex0, w	
comp_400
	bnc	no_400	
sub_400
	bsf	bcd1, 2
	movlw	0x90
	subwf 	hex0, f
	movlw	0x01
	subwfb	hex1, f

bcd_convert_400
no_400					;the top byte can only be 0 or 1 here
	btfsc  	hex1, 0
	bra	sub_200	
bcd_convert_256
	movlw	0xc8
	subwf 	hex0, w	
	bnc	no_200	
sub_200
	bsf	bcd1, 1
	movlw	0xc8
	subwf 	hex0, f
	movlw	0x00			
	subwfb	hex1, f
bcd_convert_200
no_200					;the top byte can be ignored from now on
	movlw	0x64
	subwf	hex0, w	
	bnc	no_100	
	bsf	bcd1, 0
	movlw	0x64
	subwf	hex0

no_100
	movf  	hex0, w
bcd_convert_w
;if we multiply by d'51' and add d'52', and divide by 2, the msb is the number divided by 10
	movwf	hex0
	mullw	d'51'
	movlw	d'52'
	addwf	prodl, f		
	movlw	0x00	
	addwfc	prodh, f	;this should clear the carry bit
	rrcf	prodh, w	;now w is the input/10
	movwf	bcd0
	mullw	d'10'
	movf	prodl, w
	subwf 	hex0, f
	
	swapf 	bcd0, w
	iorwf  	hex0, w
	movwf	bcd0
	return
 
Last edited:
Thank you Diver300 but the codes were kinda vague without comments for me to understand what the section of the code is for.
 
The first few lines are comments that explain what the code does.

;This converts hex1:hex0 to bcd. The top half of hex1 must be zero
;the result is in bcd1:bcd0
;The maximum output is 4096

So if you start with
0x07 in hex1
0xfb in hex0

That represents 0x07fb when hex1:hex0 are read as a register pair

Now 0x07fb in hexadecimal is equal to 2043

when the code is run,
bcd1 becomes 0x20
bcd0 becomes 0x43

That represents 2043 when bcd1:bcd0 are read as a register pair. It is then quite simple to split out the individual digits of 2, 0, 4 and 3 for display.

You could also modify the code so that all the result digits were in separate registers.

There are too few comments in most of the code. I have used the labels to describe what is going on.

Here is the first section with more comments.

Code:
	clrf    bcd1			;clear bcd1 so that individual bits can be set later in the code
;The first section compares hex1:hex0 with 0x0FA0 which is 4000 
;First hex1 is compared with 0x0F
;unless hex1 = 0x0F there is no need to compare hex0 with anything,
;so the comparison with hex0 may be skipped.
;If hex1:hex0 is 4000 or more, 4000 is subtracted, 
;and the bit is set in bcd1 that represents 4000

	movlw	0x0f
	subwf 	hex1, w		;compare hex1 with 0x0F
	bnz	comp_4000	;skip comparing hex0 unless hex1 = 0x0F
	movlw	0xa0		
	subwf 	hex0, w		;compare hex0 with 0xA0
comp_4000
	bnc	no_4000		;skip subtract section if hex1:hex0 is less than 4000

sub_4000			;The code gets here when hex1:hex0 can have 4000 subtracted
				;so that is done, and the marker is set in bcd1

	bsf	bcd1, 6		;set the bit in the output register to show that 4000 is included
	movlw	0xa0
	subwf 	hex0, f		;subtract 0xA0 from hex0
	movlw	0x0f
	subwfb	hex1, f		;subtract 0x0F (with borrow) from hex1

;that has subtracted 4000 from hex1:hex0,
; and added the bit that represents 4000 to bcd1
 
no_4000	
;and now we go on to do the same with all the other bits in bcd1:bcd0

I suggest that you run this code in the MPLAB simulator and you will be able to see what it does.
 
Last edited:
LFSR FSR0,AscBfr0 ;lsd 1st
movwf FSR0 ;pointer to ascbfr
movlw 0X05 ;digits to do
movwf Digit_Cnt
AdjLoop:
rlcf INDF0,F ;shift ASCII byte in [INDF0] 1 bit left
movlw 0X0A ;decimal 10
subwf INDF0,W ;W=[INDF0]-10

btfsc STATUS,C ;skip if no carry, its a negative result
movwf INDF0 ;only save if postive result

May I know what this part does? Can't find LFSR in the instruction set and looked it up but still don't quite get what it does here.. Same with the INDF0.. Is it possible to make a flow as to what is happening for the overall codes? Never used the rlcf and rrcf instructions before but I have a rough idea of how to do it.. as for the purpose of it being in the codes as well I'm blur.. I've modified the code as you told me to and it works as desired but would love to know exactly how is it getting there.
 
May I know what this part does? Can't find LFSR in the instruction set and looked it up but still don't quite get what it does here.. Same with the INDF0.. Is it possible to make a flow as to what is happening for the overall codes? Never used the rlcf and rrcf instructions before but I have a rough idea of how to do it.. as for the purpose of it being in the codes as well I'm blur.. I've modified the code as you told me to and it works as desired but would love to know exactly how is it getting there.

hi,
I just looked thru the 18F4580 datasheet, I can see these Instructions.??

The code I posted is for the 18F1320, so there maybe small differences.
Look at the d/s Instruction set for RLCF and RRCF operation
 

Attachments

  • AAesp01.gif
    AAesp01.gif
    78 KB · Views: 263
  • AAesp02.gif
    AAesp02.gif
    14 KB · Views: 268
Last edited:
Oppss sorry about that, seems I've missed it all this while.. my bad. >_< Going through the codes again.
 
On the 18F series, there is a lot more memory than can be addressed with a single byte.

The there are three FSR registers, FSR0, FSR1 and FSR2 that are 16 bit registers. They are the indirect addressing registers, so when the code says

rclf INDF0, F

what happens is that the register that is pointed at by FSR0 is rotated left.

For example, if FSR0 contains 0x0123, then rclf INDF0, F will rotate register 0x0123

The LFSR instruction is a single instruction that loads the FSR register with the required value.

movwf FSR0 ;pointer to ascbfr
This is wrong. The movwf instruction moves the 8 bit contents of the W register to the named register. However, FSR0 is not the name of either of the 8 bit halves of the FSR0 register. They are called FSR0L and FSR0H. The instruction as written will move the contents of the W register into register 0, depending on how the banking register is set. That is not what the writer intended.

I think that the writer intended:-
LFSR FSR0,AscBfr0 ;lsd 1st
movwf FSR0L ;pointer to ascbfr
which would make a lot more sense, as there would be several AscBfr registers and the value in W would control which one was used for the following code.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top