gnupic: Re: [gnupic] function pointers? with gplink)


Previous by date: 21 May 2005 14:39:56 +0100 Re: [gnupic] function pointers? with gplink), Bill Freeman
Next by date: 21 May 2005 14:39:56 +0100 gpsim/gpasm assertions, Scott Dattalo
Previous in thread: 21 May 2005 14:39:56 +0100 Re: [gnupic] function pointers? with gplink), Bill Freeman
Next in thread:

Subject: Re: [gnupic] function pointers? with gplink)
From: Iain Dooley ####@####.####
Date: 21 May 2005 14:39:56 +0100
Message-Id: <428FC883.6050409@iaindooley.com>


Bill Freeman wrote:
> [...]
> 
> 	Let me preface this by confessing that I, too, have only used
> 12 and 14 bit devices.  However, assuming that I understand what you
> are trying to do, there are a few bugs here.
> 
> 	Assuming that all your other ducks are in a row, if theses are
> truely functions, that is, they come back with a return instruction,
> then the main problem is that writing PCL is like a goto, it is NOT
> like a call.  (If these functions are only "called" from here, an
> alternate fix would be to put a lable on the decfsz and have the
> "functions" branch there instead of doing a return.)  Try something
> like:
> 
> 	; We presume that count is already set to the table size
> 	; Count ranges from  N to 1, need  N-1 to 0, thus:
> 	lfsr	FSR0,function_table - 1
> loop:
> 	movf	count, W
> 	call	dispatcher
> 	decfsz	count, F
> 	bra	loop
> 	; All functions have been called
> 	goto	next_code	; or maybe return
> 
> ; This dispatcher code can go anywhere since the 16 bit PIC call
> ; instruction can call anywhere.
> dispatcher:
> 	movff	PLUSW0, PCL
> 
> 	This will call the functions in the table in reverse order.
> Calling dispatcher saves the address of the decfsz on the stack, so
> that when the function returns, that's where it goes.  The movff does
> indeed behave as a jump to the function.

that's great!! so you can do a call with no return in order to use the return stack. brilliant. i actually intend to use this to interpret user button input. i'm putting a keypad through a line decoder onto PORTB and going to use the RB interrupt to read the value in. depending on what button the user pressed, i will call a different function. i'm using a 4-16 line decoder, so i'll have an array of 16 function pointers and place them so that whatever button press comes in, i just have to dispatch to the appropriate function, so i'll be using:

;ASSUMES THAT THE VALUE OF PORTB HAS BEEN LOADED INTO current_button BY AN ISR
lfsr	FSRO,function_table	;the array of function addresses
movff	PORTB,temp_location	;get the current button press
swapf	current_button		;we want the high four bits so..
m_andlf	0x0F,temp_location	;get rid of the rest
movf	temp_location,W		;put the value from 0 - 15 into WREG
call	dispatcher		;call the appropriate handler function

> [...]
> 
> 	The other assumptions that I mentioned include that all
> functions are on the same page (their addresses differ only in the low
> 8 bits) and that PCLATU and PCLATH have been set correctly for them
> outside of this code.  You could include bytes for them in the table,
> and put two or three movff instructions at dispatcher instead of 1 to
> load them before the final one writes PCL if you want to avoid this
> restriction.

i think the linker will never try to put a contiguous code segment on separate pages, so if i keep my handler functions in the same module as the button interpreter it shouldn't be a problem.

> 	The big assumption that is unusual is that the table must
> be located in RAM (gpfrs). If that's  what you want, well and good.
> But if you meant for the table to be in program space (ROM), then
> you want to follow the MicroChip dispatch table examples

actually, i put my table in an idata segment and use the attached code to transfer into data memory using the _cinit table.

> [...]

thanks very much for all your help.

iain

;*******************************************************************************
;
;    File Name: idata.asm
;
;    Assembly language code to initalise IDATA sections on PIC18xxx family.
;
;    Vers:  1.0
;    Date:  28-Apr-2004
;    Name:  Rob Middleton (Mechatronics 2 student)
;
;    Bugs:  --
;
;    ToDo:  --
;
;    Revision History
;       Vers    Date         Who?       Revision Detail?
;       1.1     11-Apr-2005  DCR        Clarified comments.
;       1.0     28-Apr-2004  RM         Released
;
;*******************************************************************************
;
;
    processor 18f452
    include "p18f452.inc"
    
    EXTERN  _cinit          ; see comments about _cinit in subroutine header


;===============================================================================
;
;       Declare Variables in RAM
;
;===============================================================================

; init_vars does not really need to be in Access RAM - and could be OVR - but
; this is easiest for now.
init_vars UDATA_ACS

init_num_sections   RES 2   ; nonsensical to use more than 2 bytes due to
init_tblptr         RES 3   ; pointer to the current position in the
                            ; _cinit table.

; init_cur variables refer to the current IDATA section.
init_cur_size       RES 2   ; address space allows max addressing of 2 bytes
                            ;  - thus the size must be a max of 2 bytes.
init_cur_from_addr  RES 3   ; address space allows max of 3 bytes (prog memory)
;init_cur_to_addr   RES 2   ; not needed as can be copied directly into FSR.



;===============================================================================
;
;    Subroutine: idata_init
;
;    This subroutine initialises variables in IDATA sections by copying
;    their initialisers from ROM.
;
;    Input:
;       The linker generates '_cinit', which is a table defining sections of
;       contiguous data (initialisers) to be copied from ROM to the
;       corresponding locations in RAM. Use of IDATA directives in assembly
;       language programs will automatically define this table. The table looks
;       something like this:

;         +==========================+==========================+
;  _cinit | init_num_sections (low)  | init_num_sections (high) |
;         +==========================+==========================+-----+
;         | init_cur_from_addr (low) | (high) | (higher) |  (highest) |
;         +--------------------------+--------+----------+------------+
;         | init_cur_to_addr (low)   | (high) | (higher) |  (highest) |   Section(N)
;         +--------------------------+--------+----------+------------+
;         | init_cur_size (low)      | (high) | (higher) |  (highest) |
;         +==========================+========+==========+============+
;         |            .             |   .    |     .    |     .      |
;
;       That is, the table has a 2-byte header containing a value 'N', the
;       number of sections to follow. N is stored least significant byte first.
;       Then follow N sections of 12 bytes each.
;
;    Usage:
;       EXTERN  idata_init
;       call    idata_init
;
;       NOTE - this function should be called as the first stage of
;       initalisation of a user program. It therefore does not attempt to
;       save and restore the values of Special Function Registers that it
;       modifies. These include TBLPTR, TABLAT, FSR1, WREG, STATUS. These
;       SFRs are, however, cleared before exit.
;
;    Output: No direct output is generated.
;
;    Side effects:
;       This subroutine modifies data memory, but this is its desired behaviour.
;       It copies initialisers from ROM to the correct locations in RAM so that
;       any variables declared in IDATA sections have their correct initial
;       values before the user program begins.
;
;       The following SFRs are cleared before the subroutine returns:
;           TBLPTR, TABLAT, FSR1, WREG, STATUS
;
;    Memory: Program Memory: ?? bytes
;            Data Memory:    ?? bytes
;
;-------------------------------------------------------------------------------
idata_init_code CODE

idata_init:
    GLOBAL idata_init               ; export symbol to other objects
    ; Load the 3 byte location of the start of the _cinit table into the
    ; Table Pointer registers.
    MOVLW   low _cinit
    MOVWF   TBLPTRL
    MOVLW   high _cinit
    MOVWF   TBLPTRH
    MOVLW   upper _cinit
    MOVWF   TBLPTRU

    ; Read the first two bytes from the _cinit table. These bytes are the number
    ; 'N' of init sections to follow. Two bytes stored least significant first.
    TBLRD   *+
    MOVFF   TABLAT, init_num_sections
    TBLRD   *+
    MOVFF   TABLAT, 1 + init_num_sections

    ; We now know the number of sections in the _cinit table. Loop through each.
    ; The number of sections is two-byte unsigned, stored LSB first.
    MOVF    init_num_sections,F       ; dummy op to set status bits
    BZ      init_dec_high_byte

init_loop:
; Read in the info about the current init section.

    ; Get the ROM "From" address.
    TBLRD   *+
    MOVFF   TABLAT, init_cur_from_addr
    TBLRD   *+
    MOVFF   TABLAT, 1 + init_cur_from_addr
    TBLRD   *+
    MOVFF   TABLAT, 2 + init_cur_from_addr
    TBLRD   *+                      ; discard the higest byte. Not needed for
                                    ; our 21 bit program address space.

    ; Get the RAM "To" address - and set FSR1 to point to this.
    TBLRD   *+
    MOVFF   TABLAT, FSR1L
    TBLRD   *+
    MOVFF   TABLAT, FSR1H
    TBLRD   *+      ; and discard 2 highest bytes outside data address space.
    TBLRD   *+

    ; Get the number of bytes to copy.
    TBLRD   *+
    MOVFF   TABLAT, init_cur_size
    TBLRD   *+
    MOVFF   TABLAT, 1 + init_cur_size
    TBLRD   *+
    TBLRD   *+

    ; Save the current table pointer so we can continue
    ; to walk the _cinit table later.
    MOVFF   TBLPTRL, init_tblptr
    MOVFF   TBLPTRH, 1 + init_tblptr
    MOVFF   TBLPTRU, 2 + init_tblptr

    ; Set the table pointer to the ROM "from" address.
    MOVFF   init_cur_from_addr, TBLPTRL
    MOVFF   1 + init_cur_from_addr, TBLPTRH
    MOVFF   2 + init_cur_from_addr, TBLPTRU

; Copy init_cur_size bytes from program memory to data memory.
; init_cur_size is two bytes long - hence the strange looking
; decrementing for loop.

    ; If low byte is zero go to init_chk_high_byte
    MOVF    init_cur_size,F       ; dummy op to set status bits
    BZ      init_cur_dec_high_byte

init_cur_dec_low_byte:
    DECF    init_cur_size,F
    TBLRD   *+
    MOVFF   TABLAT, POSTINC1
    BNZ init_cur_dec_low_byte

init_cur_dec_high_byte:
    MOVF    1 + init_cur_size,F
    BZ      init_cur_dec_done
    DECF    1 + init_cur_size,F
    BRA     init_cur_dec_low_byte

init_cur_dec_done:
    ; Restore Table Pointer so we can continue walking the cinit table.
    MOVFF   init_tblptr, TBLPTRL
    MOVFF   1 + init_tblptr, TBLPTRH
    MOVFF   2 + init_tblptr, TBLPTRU

    DECF    init_num_sections,F
    BNZ     init_loop

init_dec_high_byte:
    MOVF    1 + init_num_sections,F
    BZ      init_dec_done
    DECF    1 + init_num_sections,F
    BRA     init_loop

init_dec_done:
    ; Clear all used SFRs before returning
    CLRF    TBLPTRL                 ; clear Table Pointer and Table Latch
    CLRF    TBLPTRH
    CLRF    TBLPTRU
    CLRF    TABLAT
    LFSR    1, 0x0000               ; clear FSR1
    CLRF    WREG
    CLRF    STATUS

    RETURN

;-------------------------------------------------------------------------------

    end

Previous by date: 21 May 2005 14:39:56 +0100 Re: [gnupic] function pointers? with gplink), Bill Freeman
Next by date: 21 May 2005 14:39:56 +0100 gpsim/gpasm assertions, Scott Dattalo
Previous in thread: 21 May 2005 14:39:56 +0100 Re: [gnupic] function pointers? with gplink), Bill Freeman
Next in thread:


Powered by ezmlm-browse 0.20.