## How do I set up a vector table?

23 posts / 0 new
Author
Message

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

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...

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
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

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

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

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_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_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

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

Here's a little thing I use:

; -----------------------------------------------------------
;
;    parameters:
;      R0 - offset of jump address
;
;    returns:
;      none
;
;    usage:
;      R0 <-- offset
;      call jumptable
;
;
;    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
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

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

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
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)

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
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
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

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

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

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

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

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!

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

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

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

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

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

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)

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
void jump_to(unsigned char);
}