How do I set up a vector table?

Go To Last Post
23 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Good evening, ( Toronto, Canada )

I need to set up a vector table that will allow me to vector or jump to a routine based on the value in a register. ie. ( If a register contains 0x01 a jump to routine A is made. If the register contains 0x02 then a jump to routine B is made....). I have to select between 24 routines and currently I am parsing though and checking the register value up to 24 times before finding the correct destination. A primer from someone in the know would be appreciated.

Any suggestions?

ATmega640
ASM

Thanks for the help.

A

AVR Studio 4 Ver. 4.18 684
avr-gcc Ver. 4.3.0
ISIS 7
ELECTRA

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Try the current data sheet page 106 section 14.2 Reset and Interrupt Vector placement. The AVR assembly code is right there.

ATmega640/1280/1281/2560/2561 Data Sheet:
http://www.atmel.com/dyn/resourc...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The key is to use the ICALL (or IJMP) instruction, which calls (or jumps to) the address in the Z register pair. So in pseudocode (I don't want to have to look up all the real instructions and syntax :)) you'd want

  add  r1,r1  ;offset times 2 to account for 16 bit addresses
  movi X,#vector_table ;get base address of your vector table
  add  X,r1  ;add 8-bit offset to 16-bit base address
  mov  Z,[X]  ;use X to fetch 16-bit address into Z
  icall  ;or ijmp, whatever you want to do

vector_table:
  dw  #routine_a
  dw  #routine_b
  ....

Note that this assumes a 0-based index.

Mike

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A follow-up to the OP's original question would be how do you know where the various routines are located (GCC) and how do you put that data in the jump table?

-Tony

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Guys,

This is not related to interrupts. I need this to act as a switch statement would in C. But I think I have a handle on this from Mike's description. I will try to work out the vector table and see what I come up with.

Thanks,

A

AVR Studio 4 Ver. 4.18 684
avr-gcc Ver. 4.3.0
ISIS 7
ELECTRA

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A quick copy and paste from the PDF file Mike pointed to turns that text into my standard format interrupt vector file. This only needs to be done once and then saved into your library folder to be reused.

Here is a sample project of it's use.

#define temp r24
#define save_sreg r15

.include "m640def.inc"
.include "c:\avr\lib\int_v_m640_1280_1281_2560_2561.asm"

main:
	rjmp	main

init:
;initialise stuff here
	ret



ISR_INT0:			;IRQ0 Handler

	in		save_sreg,SREG		;Save Status register
;Put your ISR service code here	
	out		SREG,save_sreg		;Restore Status register
	reti

ISR_INT1:			;IRQ1 Handler

ISR_INT2:			;IRQ2 Handler

ISR_INT3:			;IRQ3 Handler

ISR_INT4:			;IRQ4 Handler

ISR_INT5:			;IRQ5 Handler

ISR_INT6:			;IRQ6 Handler

ISR_INT7:			;IRQ7 Handler

ISR_PCINT0:			;PCINT0 Handler

ISR_PCINT1:			;PCINT1 Handler

ISR_PCINT2:			;PCINT2 Handler

ISR_WDT:			;Watchdog Timeout Handler

ISR_TIM2_COMPA:		;Timer2 CompareA Handler

ISR_TIM2_COMPB:		;Timer2 CompareB Handler

ISR_TIM2_OVF:		;Timer2 Overflow Handler

ISR_TIM1_CAPT:		;Timer1 Capture Handler

ISR_TIM1_COMPA:		;Timer1 CompareA Handler

ISR_TIM1_COMPB:		;Timer1 CompareB Handler

ISR_TIM1_COMPC:		;Timer1 CompareC Handler

ISR_TIM1_OVF:		;Timer1 Overflow Handler

ISR_TIM0_COMPA:		;Timer0 CompareA Handler

ISR_TIM0_COMPB:		;Timer0 CompareB Handler

ISR_TIM0_OVF:		;Timer0 Overflow Handler

ISR_SPI_STC:		;SPI Transfer Complete Handler

ISR_USART0_RXC:		;USART0 RX Complete Handler

ISR_USART0_UDRE:	;USART0,UDR Empty Handler

ISR_USART0_TXC:		;USART0 TX Complete Handler

ISR_ANA_COMP:		;Analog Comparator Handler

ISR_ADC:			;ADC Conversion Complete Handler

ISR_EE_RDY:			;EEPROM Ready Handler

ISR_TIM3_CAPT:		;Timer3 Capture Handler

ISR_TIM3_COMPA:		;Timer3 CompareA Handler

ISR_TIM3_COMPB:		;Timer3 CompareB Handler

ISR_TIM3_COMPC:		;Timer3 CompareC Handler

ISR_TIM3_OVF:		;Timer3 Overflow Handler

ISR_USART1_RXC:		;USART1 RX Complete Handler

ISR_USART1_UDRE:	;USART1,UDR Empty Handler

ISR_USART1_TXC:		;USART1 TX Complete Handler

ISR_TWI:			;2-wire Serial Handler

ISR_SPM_RDY:		;SPM Ready Handler

ISR_TIM4_CAPT:		;Timer4 Capture Handler

ISR_TIM4_COMPA:		;Timer4 CompareA Handler

ISR_TIM4_COMPB:		;Timer4 CompareB Handler

ISR_TIM4_COMPC:		;Timer4 CompareC Handler

ISR_TIM4_OVF:		;Timer4 Overflow Handler

ISR_TIM5_CAPT:		;Timer5 Capture Handler

ISR_TIM5_COMPA:		;Timer5 CompareA Handler

ISR_TIM5_COMPB:		;Timer5 CompareB Handler

ISR_TIM5_COMPC:		;Timer5 CompareC Handler

ISR_TIM5_OVF:		;Timer5 Overflow Handler

ISR_USART2_RXC:		;USART2 RX Complete Handler

ISR_USART2_UDRE:	;USART2,UDR Empty Handler

ISR_USART2_TXC:		;USART2 TX Complete Handler

ISR_USART3_RXC:		;USART3 RX Complete Handler

ISR_USART3_UDRE:	;USART3,UDR Empty Handler

ISR_USART3_TXC:		;USART3 TX Complete Handler

	reti

Note that the first ISR for ISR_INT0 is what I use in another standard file for all ISRs. Of course they can all be pointed to a do nothing instruction as the rest of the ISRs show.

Attachment(s): 

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
how do you put that data in the jump table?
You don't, the assembler will know what address the ISr is loated and fill in the address. See above sample.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's a little thing I use:

; -----------------------------------------------------------
; jumptable - jump to address in flash list
;
;    parameters:
;      R0 - offset of jump address
;
;    returns:
;      none
;
;    usage:
;      R0 <-- offset
;      call jumptable
;      .dw        address0, address1, address2, ...
;
;      if offset = 0, jump to address0
;      if offset = 1, jump to address1, etc.
;
;    note: these are jumps, not calls, to the addresses, but
;          it is a call to jumptable

jumptable:
        push   YL          ; save scratch regs
        push   YH
        push   ZL
        push   ZH
        push   r16

        in     YL,SPL      ; get current stack pointer
        in     YH,SPH

        ldd    ZL,Y+7      ; low byte of orig rtn addr
        ldd    ZH,Y+6      ; high byte of orig rtn addr
        add    ZL,r0       ; add offset
        brcc   njcar       ; if no carry, done
        inc    ZH          ; add carry to high
njcar:  lsl    ZL          ; multiply by 2 for byte address
        rol    ZH          ; shift across carry

        lpm    r16,Z+      ; low byte of address
        std    Y+7,r16     ; replace our return address
        lpm    r16,Z       ; high byte of address
        std    Y+6,r16     ; finish off our return address

        pop    r16
        pop    ZH
        pop    ZL
        pop    YH
        pop    YL
        ret                ; jump to modified address

It patches the stack, and then returns which takes it to the selected address. It's not too efficient, but it does preserve all registers, which was important when I wrote it. As mentioned, icall or ijmp would do.

An example of its use would be:

    
    rcall  jumptable
    .dw    r0_is_0
    .dw    r0_is_1
    etc.

r0_is_0:
    do whatever
    rjmp   onward
r0_is_1:
    do whatever
    rjmp   onward

and so on

onward:
    next instruction

So this routine works if your register (r0) contains 0 to whatever with no breaks. There are a couple of related ditties on my website (see signature) in the download code under utilities. One of them takes an 8 bit value (the other a 16 bit value) and tries to match it to a value in a list, then jumps to the specified address. It uses a similar trick of calling the routine, patching the stack, and returning to make the jump to the appropriate address. So they're table lookup and jump. Have a look.

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Spamiam wrote:
how do you know where the various routines are located (GCC) and how do you put that data in the jump table?
You do it essentially the same way as zbaird has shown but the syntax for the gnu assembler is slightly different than that for the Atmel assembler. Instead of .dw you use .word as shown below. The pm relocatable expression modifier converts the byte address of the function to a word address as is required for ijmp/icall or ret.

  .word  pm(funcName)

Note that you needn't declare funcName (or any other external function/variable names) because the gnu assembler assumes that any symbol that it doesn't know about is external.

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I handle my subroutines like this:

;======================================
;software "stack" for indirect calls
;======================================
D1: rjmp Display1
rjmp Display2
rjmp Display3
rjmp Display4
rjmp Display5
rjmp Display6
rjmp Display7
rjmp Display8
rjmp Display9
rjmp Display0

The main works like:
Display(n); // n = number to show
n++;

main:
;[some code]
	LDI ZL, low(D1)
	LDI ZH, high(D1);load Pointer Z with the label D1
	ADD ZL, w	;add offset to Z 
	icall		;call subroutine to which Z points
	inc w		;next time Z will point to the next subroutine
;[more code]
rjmp main

Definition of subroutines:

Display0:
;[some code]
RET

Display1:
;[code again]
RET
.
.
.

(w=R16, initialized as zero, ZL and ZH could be initialized only once)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nephazz, you may want to consider what happens when D1 is at an address something like 0x02FF- 'ADD ZL, w' will overflow ZL, but you don't adjust ZH. You will icall 'somewhere', but not where you intended.

Here's an attempt (gcc asm)-

//test.S
func1:
    ret
func2:
    ret

//jmp to jmp_table[r24]
jmp_to:
    ldi r30,lo8(pm(jmp_table))
    ldi r31,hi8(pm(jmp_table))
    clr r25
    lsl r24 //jmp takes 2 words
    add r30,r24
    adc r31,r25
    ijmp
jmp_table:
    jmp func1 //jmp_table[0]
    jmp func2 //jmp_table[1]

and this is a function address table version-

//test.S
func1:
    ret
func2:
    ret

//ijmp to *func_table[r24]
ijmp_to:
    ldi r30,lo8(func_table) //bytes
    ldi r31,hi8(func_table)
    clr r25
    lsl r24 //2 bytes per address
    add r30,r24
    adc r31,r25
    lpm r24,Z+
    lpm r31,Z
    mov r30,r24
    ijmp
func_table:
    .dc.w pm(func1) //func_table[0]
    .dc.w pm(func2) //func_table[1]
Last Edited: Mon. Dec 22, 2008 - 06:58 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nephazz's idea is good, but there's a potential error. You have to allow for carry into zh in that add, since you don't know how well behaved the address of d1 will be.

beat to the draw by curt, alas!

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

andrew99 wrote:
I need this to act as a switch statement would in C.

Umm - use switch() in C then? ;)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson,
To C or not to C. If the job could be done in C, I would have used it. :D

John S.
The interrupt vector table will not help me with a call or jump dependent on an variable in a register. :wink:

All,
I will look through your suggestions and make one of them work for me.

Thanks,

A

AVR Studio 4 Ver. 4.18 684
avr-gcc Ver. 4.3.0
ISIS 7
ELECTRA

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

To C or not to C. If the job could be done in C, I would have used it.

I'd be intrigued to know what cannot be done in C - no really. This isn't starting a war but enquiring minds like to know these things!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think I've seen GCC push an address onto the stack and then do a 'ret' to handle a switch - does that make sense?

Nigel Batten
www.batsocks.co.uk

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson,

Man you really like to stir things up :lol:

I can get by in C or asm but I am expert at neither so I have to use the tool that is easiest for
me to use. In this particular case I need to grab bytes off of a PC104 bus using interrupts. I am grabing the byte in the vector table. I can do this without much head scratching in asm or I can bug you guys for a solution in C. :)

Which would you prefer?

Thanks for the input.

A

AVR Studio 4 Ver. 4.18 684
avr-gcc Ver. 4.3.0
ISIS 7
ELECTRA

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's a related method (in C) that I use in place of unwieldy switch statements...
It calls a function from a look-up table...

// Lookup table
char_handler * PROGMEM f_ch_control[ 32 ] =
{
	ch_nul,		// 0
	ch_control_default ,// 1
	ch_control_default ,// 2
	ch_control_default ,// 3
	ch_control_default ,// 4
	ch_control_default ,// 5
	ch_control_default ,// 6
	ch_control_BEL ,	// 7
	ch_control_BS ,		// 8
	ch_control_TAB ,	// 9
	ch_control_LF ,		// 10
	ch_control_default ,// 11
	ch_control_FF ,		// 12
	ch_control_CR ,		// 13
	ch_control_default ,// 14
	ch_control_default ,// 15
	ch_control_default ,// 16
	ch_control_default ,// 17
	ch_control_default ,// 18
	ch_control_default ,// 19
	ch_control_default ,// 20
	ch_control_default ,// 21
	ch_control_default ,// 22
	ch_control_default ,// 23
	ch_control_default ,// 24
	ch_control_default ,// 25
	ch_control_default ,// 26
	ch_control_ESC ,	// 27
	ch_control_default ,// 28
	ch_control_default ,// 29
	ch_control_default ,// 30
	ch_control_default ,// 31
};

and this is how it's used:

	char_handler *my_handler = (char_handler *) pgm_read_word( &f_ch_control[ g_char ] ) ;
	my_handler();

It's useful to me because it has a fixed number of cycles, whichever function is chosen.

Nigel Batten
www.batsocks.co.uk

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

andrew99 wrote:
If the job could be done in C, I would have used it.
Surely, some portion of the project, perhaps most or all of it, could be implemented using C. For the parts for which it is preferable (for whatever reason) to use assembly language, it is quite simple to implement C-callable routines in AVR assembly language using the GNU assembler (gas).

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

dkinzer,

The AVR-GCC assembly interface is difficult to use at best. With the free tools I will stick with one language at a time whenever possible thank you.

Add to that the fact that you can fill a thousand threads with post about which is the better language to use.

A

AVR Studio 4 Ver. 4.18 684
avr-gcc Ver. 4.3.0
ISIS 7
ELECTRA

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

The AVR-GCC assembly interface is difficult to use at best.

Most people make the mistake of thinking it's going to be easier to mixed C/Asm by using inline Asm within .c files in GCC - this is not the case, inline asm is the preserve of the gurus alone. Us mere mortals would split Asm in GCC into separate .S files in which case the ONLY bit of "interface" you need to know is the ABI documented at:

http://www.nongnu.org/avr-libc/u...

(in fact even easier is to expose globals between C and Asm and forget register or stack parameter passing but just pass things in the globals)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

andrew99 wrote:
The AVR-GCC assembly interface is difficult to use at best.
I agree that the inline assembly syntax is arcane (but effective, nonetheless). That is why it is usually recommended to implement certain critical parts in pure assembly language, placing the source code in a separate .S file. (This is what I intended to suggest; perhaps I phrased it poorly.)

As long as your C-callable assembly language functions comport with the requirements of the register usage conventions of the compiler it is no more difficult to implement these separate assembly language functions than it is when working in a pure assembly language implementation.

Adopting the mixed C/assembly language gives you the best of both worlds since you can pick and choose which parts to implement in C and which to implement in assembly language. As the application evolves over time, you can adjust the implementation as necessary moving more to C or more to assembly language as you deem appropriate.

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

Last Edited: Tue. Dec 23, 2008 - 12:48 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

and this is how easy it is to use my previous asm routines in C (making the asm functions .global first)-

void jump_to(unsigned char);
void ijmp_to(unsigned char);
int main(){
  ijmp_to(1);
  jmp_to(0);
}

(notice why I used r24 in the examples, also notice no called-saved registers were used- Cliff's link will show register usage expected by compiler).