vectored jump table

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

Hello All,

what's the easiest way to do a vectored jump (in asm) ?

I'm used to doing it like this for the PIC:

movfw variable ; read offset
addwf PC, F ; add it to the program counter
(jump to here if 0) ; end up here
(jump to here if 1) ; etc

which is really neat and fast. How would you achieve the same thing for the AVR ?

Cheers

Dren

<º))))><

I am only one lab accident away from becoming a super villain.

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

IJMP

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

Thanks for your impressively fast response :)

So that would be something like :

CLR R31
IN R30, variable_addr
IJMP
(jump to here if variable = 0) ; end up here
(jump to here if variable = 1) ; etc

How do I load the Z reg (R30:R31) with the current PC ?

(assuming that R31 is the same for all values ie the table isn't across a page)

<º))))><

I am only one lab accident away from becoming a super villain.

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

If I use it in an ISR then it becomes :

  	   PUSH R0      
	   PUSH R30      
	   PUSH R31      
	   in R31, 0x3F      		// back up STATUS FLAGS 
	   PUSH R31      

	   ldi R30, (jump_table)       	// set up Z reg LSB	
	   ldi R31, (jump_table>>8)       	// set up Z reg MSB	
	   in R0, offset       
	   add R30, R0      		// add offset
	   clr R0\n")
	   adc R31, R0      		// add offset
	   ijmp      				// jump to correct volume	

jump_table:
	(option 0)
	(option 1)
	(etc)

	   POP R31      
	   out 0x3F, R31       	// restore STATUS FLAGS 
	   POP R31      
	   POP R30      
	   POP R0      
  	   reti       

Surely there must be a better way of doing this ?

<º))))><

I am only one lab accident away from becoming a super villain.

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

Quote:

How do I load the Z reg (R30:R31) with the current PC ?

First idea that comes to mind:

  CALL here
here:
  POP ZH
  POP ZL

(though you better check I got the endianess of that right!) but as the table is a little after this you'd still be doing an ADIW to add an offset.

Cliff

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

good idea. You've always been incredibly helpful (and useful too) - I'll have to buy you a beer as some point. I'm current on site in Basildon, Essex

<º))))><

I am only one lab accident away from becoming a super villain.

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

Quote:
good idea.

I don't see why. The CALL/POP/POP takes 8 cycles (though you could use RCALL to make it 7), plus it needs to be adjusted to the actual address of the table, and you still need to add in the offset into the table. Your code from the first LDI through the IJMP is only 8 cycles total. I don't know why you think that this is so bad.

Regards,
Steve A.

The Board helps those that help themselves.

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

clawson wrote:
Quote:

How do I load the Z reg (R30:R31) with the current PC ?

First idea that comes to mind:

  CALL here
here:
  POP ZH
  POP ZL

(though you better check I got the endianess of that right!) but as the table is a little after this you'd still be doing an ADIW to add an offset.

Two LDIs would be faster and smaller.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

But how would that 'know' the current PC addr ?

<º))))><

I am only one lab accident away from becoming a super villain.

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

Quote:

But how would that 'know' the current PC addr ?

Because it's known at compile time, that is:

  LDI Zl,low8(here)
  LDI Zh,high8(here)
here:

but in that case you might as well go with your previous:

      ldi R30, (jump_table)          // set up Z reg LSB    
      ldi R31, (jump_table>>8)          // set up Z reg MSB    
      in R0, offset        
      add R30, R0            // add offset 
      clr R0\n") 
      adc R31, R0            // add offset 
      ijmp                  // jump to correct volume    

jump_table: 

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

Ah well, perhaps it wasn't such a good idea then :(

It still seems that there must be a better way of doing the vectored jump given that it's so easy in microcode. Is there a site of 'top-tips' in AVR assembler programming ? Having 32 registers sounds great, but when you can only do some operations on a small subset of them it really takes the edge off them.

<º))))><

I am only one lab accident away from becoming a super villain.

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

But you aren't really comparing apples with apples? Can you show all the setup needed on the PIC and how that compares to the AVR - perhaps show the entire ISRs you'd need to use to achieve the same thing. I don't see what's "inefficient" in the AVR way of doing thing to jump via a table. You have to load the table address into an indexing register (pair) - surely that's also required on the PIC? You have to calculate the offset into the table and add this to the index. Surely that's also required on the PIC? Then you make an indirect jump through the calculated index. Surely that's also required on the PIC? And around anything you have to save/restore any registers/machine-state that gets clobbered in the ISR - surely that's also required on the PIC?

So what makes the PIC more "efficient" (size?speed?) in doing this than AVR? And are we talking one two byte opcode or 1/2 machine cycles or what?

Maybe it's just the price you pay for using a better CPU architecture that scores in so many other ways? :lol: (but I think that discussion has already been done to death elsewhere?)

Cliff

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

Oh god let's not get too drawn into the AVR vs PIC debate. I'm just interested in getting the most from the AVR. However, answering the point (in passing ?)

Quote:
surely that's also required on the PIC? You have to calculate the offset into the table and add this to the index. Surely that's also required on the PIC?
The PIC has a FAST interrupt call that backs everything up in hardware so the PIC ISR is almost nothing, (infact it's as above plus a a 'call ISR "name" FAST' to handle the swapping with no additional clocks). 'Offset' can be stored in any one of the hundreds of RAM locations (called files) which are very similar to what the AVR refers to as 'registers'.
INTERRUPT:
	call 	ISR, FAST	; jump to ISR + auto-save STATUS, WREG, BSR
	retfie  FAST   		; return from interrupt + restore STATUS, WREG, BSR
ISR:
	movfw	Offset, A	; read vector (A= ACCESS mode so no BANKing problems)
	addwf	PCL, F, A	; jump to value (add offset to PC + store result in PC)
	(option 0)		; arrive here !
	(option 1)
	etc...

So the upshot of it is that the PIC equivalent is only about 8 clocks in total, so about 4 times shorter/faster ?
The AVR IS a pain the butt doing it this way, but then again why do it this way ? There must be other ways that the AVR has an advantage I just need to learn the specialist tricks to getting the best performance out of it. This is the first time that I've written assembler for it and I hope that it can't be as limited as it seems so far. I just need to find + learn a few top-tips and work arounds.

I'm working in Essex at the mo, I'll buy you a beer one evening if you'd like that ?

<º))))><

I am only one lab accident away from becoming a super villain.

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

As I only had a few options, here's my solution :

	cpi R16,0x03		// Offset = R16
	breq Option4		// R16 = 3 -> Option4
	brge Option5		// R16 = 4+ -> Option5
	cpi R16,0x01		// Option = R16
	breq Option2		// R16 = 1 -> Option2
	brge Option3		// R16 = 2 -> Option3
Option1:
Option2:
etc

it's up and working, but any improvements or suggestions for next time would be appreciated - thanks

<º))))><

I am only one lab accident away from becoming a super villain.

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

What PIC language is that - I'm just looking at a manual for the PIC16 opcode and in that MOVFW is a single operand instruction that is used to move W into 'register' f where 0<=f<=127 - what's the additional ", A" on your variant of this?

Similarly for ADDWF it's only a 2 operand instruction "ADDWF f,d" where 0<=f<=127 and d is 0 or 1.

I'd like to understand the syntax and what PIC is accomplishing in one cycle but clearly I'm looking at the wrong PIC:

http://ww1.microchip.com/downloa...

Cliff

PS This isn't a PICvAVR "argument" - I'm genuinely interested.

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

I'm using a PIC 18F4410 as they are cheap and it has a nice instruction set, (check out DECFSZ) great comms and plenty of I/O. The new PICs have 'access' mode, which means that you don't have to keep swapping banks (pages) all the time. The performance of the earlier PICs was really shafted by this on any decent length program.

We are using AVRs more often than PICs in products these days as they are better at some tasks, however, it's not clear cut. I'm always looking for cunning ways of finding a neater way to do something and get that bit of extra processing power, just in case we need it. I'd like to see a programmers' tip page like on piclist.com

<º))))><

I am only one lab accident away from becoming a super villain.

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

um you havent even tried the standard jmp/branch instructions most of which are 1/2/3 clock cycles

BRANCH INSTRUCTIONS !!!!!

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

Quote:
BRANCH INSTRUCTIONS !!!!!
err.. care to elaborate ? If there's a good method around or a shorter way of doing something I'm always happy to learn :)

<º))))><

I am only one lab accident away from becoming a super villain.