Tips and Tricks

nterrupts with C/C++ 

There are two ways to use interrupt with C/C++. One is using asm("") and one is using a gcc extension called __attribute__((__interrupt_handler__))

Using asm("")

The following code sequences shows how to program interrupt code. It uses a small assembly wrapper.

void hz100_int(void);     /* function prototype for asm rapper*/
void hz100_handler(void); /* function prototype for C code */

int main(void)
{
	...

	*(long *)(vbraddr+64*4) = (long)hz100_int; 
	/* enable interrupts */
	
	...
}

void hz100_handler(void)
{
	... /* c code for interrupt handler */
}

// hz100 interrupt
asm(".global hz100_int");         /* make assembly symbol hz100_int available to linker */
asm("hz100_int:link %a6,#0");
asm("moveml %a6-%d0,-(%a7)");     /* save all registers */
asm("jsr hz100_handler");         /* call c function */
asm("moveml (%a7)+,%d0-%a6");     /* restore registers */
asm("unlk %a6");
asm("rte");

The saving of al registers is needed to ensure that no register is corrupted in the interrupt routine. However, saving all registers would not be needed if the compiler would know which registers are used and generate the correct pre- and post interrupt code, including the rte instruction.. This is achieved with the __attribute__ keyword as described in the next session.

Using __attribute__((__interrupt_handler__))

When using the __attribute__((__interrupt_handler__)) there is no need to generate an assembly stub. The compiler takes care of the register usage and uses rte instead of rts instruction.

void hz100_int(void);     /* function prototype for asm rapper*/
void hz100_handler(void); /* function prototype for C code */

int main(void)
{
	...

	*(long *)(vbraddr+64*4) = (long)hz100_handler; 
	/* enable interrupts */
	
	...
}


__attribute__((__interrupt_handler__)) void hz100_handler(void) 
{
	if(hz100_flag) {
    	hz100_overrun++;
    	return;
  	}
  	hz100_flag = 1;

  	ad[0] = *(unsigned char *)0xf00000;
	/* rest c code for interrupt handler */
}

The compiler generated the following code, only saving the registers that are used. (output generated with m68k-elf-objdump -S -d -w mrmtest.s19 command)

__attribute__((__interrupt_handler__)) void hz100_handler(void) 
{
     842:	4e56 0000      	linkw %fp,#0
     846:	2f00           	movel %d0,%sp@-
  if(hz100_flag) {
     848:	4ab9 0000 8614 	tstl 8614 <hz100_flag>
     84e:	6708           	beqs 858 <hz100_handler+0x16>
    hz100_overrun++;
     850:	52b9 0000 8618 	addql #1,8618 <hz100_overrun>
    return;
     856:	6016           	bras 86e <hz100_handler+0x2c>
  }
  hz100_flag = 1;
     858:	7001           	moveq #1,%d0
     85a:	23c0 0000 8614 	movel %d0,8614 <hz100_flag>

  ad[0] = *(unsigned char *)0xf00000;
     860:	4280           	clrl %d0
     862:	1039 00f0 0000 	moveb f00000 <__stack+0xe80004>,%d0
     868:	23c0 0000 861c 	movel %d0,861c <ad>
     86e:	201f           	movel %sp@+,%d0
     870:	4e5e           	unlk %fp
     872:	4e73           	rte

Obviously, this is much faster than the first method. The same works also for C++ member functions.

class qspi {
	...
	void irq();
	...
} 

void __attribute__((__interrupt_handler__)) qspi::irq()
{
... // your code here
}

68000 absolute short address mode

One of the things of the 68000 series I never understood was the reason for
the absolute short address mode. It could save a word in the instruction stream.
Nice... addressing 32k from 0x0000 to 0x7fff and 32k from 0xffff8000 till
0xffffffff.

But... for a 68332 it can come in handy. As one knows the SIM block lies in the area of 0xfffa00-0xffffff range.
Since the 68332 only uses the 24 bits for the address decode and ignores the upper 8 bits there I can also select 0x10fffa00 to get the SIMCR register. Or 0xfffffa000. The difference is of course when you compile the following (using gcc):

    asm("bsetb #1,0xfffa19");

Results in 4 instruction words
332: 08f9 0001 00ff bset #1,fffa19 <sci_status+0xffed75>
338: fa19

while

    asm("bsetb #1,0xfffffa19");

results in 3 instruction words.
32c: 08f8 0001 fa19 bset #1,fffffa19 <sci_status+0xffffed75>

And one saves one word in the instruction stream. For time critical stuff with for example the TPU (or anything in the SIM
block) it might be worth change some of the defines when accessing a SIM, TPU, QSM or anything in the 0xfffa00-0xffffff range to 0xfffffa0-0xffffffff.

Or consider to put the fast ram starting from 0xfffff8000. The gcc does generate short address range.
The same applies equally for the first 32k of the address map.

Needless to say it does work also for an example as below.

#define QSM_BASE (0xfffffc00L)
#define QSM_SCCR1 (*(short int *) (QSM_BASE+0x0a))

void set_qsm_scc1_to_0x002c()
{
    QSM_SCCR1 = 0x002c;
}