Interrupts disable and reenable with GCC

C and PASCAL (or any other high-level languages) in here please

Moderators: exxos, simonsunnyboy, Mug UK, Zorro 2, Moderator Team

User avatar
simonsunnyboy
Moderator
Moderator
Posts: 4849
Joined: Wed Oct 23, 2002 4:36 pm
Location: Friedrichshafen, Germany
Contact:

Interrupts disable and reenable with GCC

Postby simonsunnyboy » Sun Feb 05, 2017 8:45 am

The thread topic describes all. What is the proper syntax to disable interrupts and how to reenable them if they were active with methods usable by GCC? It should work with baremetal or TOS code, using MinT calls is not desired.
Simon Sunnyboy/Paradize - http://paradize.atari.org/ - STOT: http://www.npoi.de/stot/

Stay cool, stay Atari!

1x2600jr, 1x1040STFm, 1x1040STE 4MB+TOS2.06+SatanDisk, 1xF030 14MB+FPU+NetUS-Bee

Jabber: simonsunnyboy@atari-jabber.org

mikro
Atari God
Atari God
Posts: 1284
Joined: Sat Sep 10, 2005 11:11 am
Location: Brisbane, Queensland, Australia
Contact:

Re: Interrupts disable and reenable with GCC

Postby mikro » Sun Feb 05, 2017 11:12 am

I'm not sure what do you mean by "active with methods usable by GCC" but generally you want to either inline move #0x2700,sr & friends into your C function or make ints_on() and ints_off() functions in a separate .S file.

User avatar
dml
Fuji Shaped Bastard
Fuji Shaped Bastard
Posts: 3472
Joined: Sat Jun 30, 2012 9:33 am

Re: Interrupts disable and reenable with GCC

Postby dml » Sun Feb 05, 2017 11:46 am

If you're already in supervisor mode... (fill in the mfp register blanks!)

Code: Select all

// globals
char regsold[4]; // somewhere to keep the old mfp regs
char regsnew[4] = { 0,0,0,0 }; // some new mfp regs

void mfp_setup()
{
char *pnew = regsnew;
char *pold = regsold;

__asm__ __volatile__
(
 "ori.w #0x0700,sr;"
 "move.b #0xfffffaXX.w,(%1)+;"
 "move.b (%0)+,0xfffffaXX.w;"
 "move.b #0xfffffaXXX.w,(%1)+;"
 "move.b (%0)+,0xfffffaXX.w;"
 "move.b #0xfffffaXX.w,(%1)+;"
 "move.b (%0)+,0xfffffaXX.w;"
 "move.b #0xfffffaXX.w,(%1)+;"
 "move.b (%0)+,0xfffffaXX.w;"
 "andi.w #0xfbff,sr;"
 : "+a"(pnew), "+a"(pold)
 :
 : "cc"
);
}


...and if you're not in supervisor mode, you can get there the usual way using Supexec(&mfp_setup,0,0,0,0,0);

Check the arg count - there's more than one variant of Supexec in the mint/tos system headers... And doublecheck my syntax. I type this stuff in without testing it. Minus coffee.

If the multiple "" concatenated strings isn't accepted, use a single open/close quote " and use line continuation \ between each line in the string. Or stick all the ops on one line if you prefer. GCC asm expects a single string generally...

User avatar
simonsunnyboy
Moderator
Moderator
Posts: 4849
Joined: Wed Oct 23, 2002 4:36 pm
Location: Friedrichshafen, Germany
Contact:

Re: Interrupts disable and reenable with GCC

Postby simonsunnyboy » Sun Feb 05, 2017 3:01 pm

Inlining some asm would be ok if I get the according snippets. I'm not too firm with the weird syntax of inline assembly with gcc.
Simon Sunnyboy/Paradize - http://paradize.atari.org/ - STOT: http://www.npoi.de/stot/

Stay cool, stay Atari!

1x2600jr, 1x1040STFm, 1x1040STE 4MB+TOS2.06+SatanDisk, 1xF030 14MB+FPU+NetUS-Bee

Jabber: simonsunnyboy@atari-jabber.org

User avatar
mfro
Atari Super Hero
Atari Super Hero
Posts: 674
Joined: Thu Aug 02, 2012 10:33 am
Location: SW Germany

Re: Interrupts disable and reenable with GCC

Postby mfro » Sun Feb 05, 2017 3:18 pm

You basically just set the CPU's interrupt mask higher than the interrupt level you want to allow. If you want to disable all interrupts (except NMI), just set level 7.
The following should be usable as a drop-in into any C code (just put it into one of your header files). It's probably suboptimal regarding performance, but should work on anything m68k (including ColdFire).

Code: Select all

inline uint32_t set_ipl(uint32_t ipl)
{
    uint32_t ret;

    __asm__ __volatile__(
        "       move.w  sr,%[ret]\r\n"          /* retrieve status register */
        "       andi.l  #0x07,%[ipl]\n\t"       /* mask out ipl bits on new value */
        "       lsl.l   #8,%[ipl]\n\t"          /* shift them to position */
        "       move.l  %[ret],d0\n\t"          /* retrieve original value */
        "       andi.l  #0x0000f8ff,d0\n\t"     /* clear ipl part */
        "       or.l    %[ipl],d0\n\t"          /* or in new value */
        "       move.w  d0,sr\n\t"              /* put it in place */
        "       andi.l  #0x0700,%[ret]\r\n"     /* mask out ipl bits */
        "       lsr.l   #8,%[ret]\r\n"          /* shift them to position */
        : [ret] "=&d" (ret)     /* output */
        : [ipl] "d" (ipl)       /* input */
        : "d0", "cc"  /* clobber */
    );
    return ret;
}

/*
 * usage
 */
...
    /*
    * disable interrupts
    */
 
    uint32_t  old_ipl;
 
    old_ipl = set_ipl(7);
 
    /*
    * do whatever you want to do with interrupts disabled
    */

    ...
   
    /*
     * enable interrupts again (restore saved interrupt level)
     */
    set_ipl(old_ipl);


Make sure you call it in supervisor mode.
Last edited by mfro on Sun Feb 05, 2017 3:53 pm, edited 1 time in total.

User avatar
dml
Fuji Shaped Bastard
Fuji Shaped Bastard
Posts: 3472
Joined: Sat Jun 30, 2012 9:33 am

Re: Interrupts disable and reenable with GCC

Postby dml » Sun Feb 05, 2017 3:21 pm

the three constraints blocks following the asm:

: A,B,C // outputs (or in+outs)
: D,E,F // inputs only
: G,H,I // clobbered registers, condition codes or memory, if any (i.e. things corrupted and not saved/restored by the code itself)

in the first constraints block (in+outs):

"+a"(pnew)

'+' means both input and output. i.e. it is read and changed by the asm. otherwise '=' would indicate an output only.
'a' means address register - any free one which the compiler can provide. 'd' would mean data register. 'r' would mean any register. 'm' would mean memory variable. 'g' would mean a general memory or register operand, whatever the compiler prefers. see gcc docs for more of these.
'(pnew)' - the C variable assigned to that address register operand

in the code:

'%0' - operand 0 (=pnew)
'%1' - operand 1 (=pold)

This is just one of many ways to use inline asm. You can name registers directly, but then you have to mark them 'clobbered' or save them yourself, which produces less efficient code. Although that would be fine for this task too.

czietz
Hardware Guru
Hardware Guru
Posts: 467
Joined: Tue May 24, 2016 6:47 pm

Re: Interrupts disable and reenable with GCC

Postby czietz » Sun Feb 05, 2017 3:45 pm

mfro wrote:The following should be usable as a drop-in into any C code (just put it into one of your header files). It's probably suboptimal regarding performance, but should work on anything m68k (including ColdFire).

Code: Select all

[...]
        : "cc"  /* clobber */
[...]



Don't you clobber D0, as well? At first glance, this doesn't matter that much because D0 is subsequently used for the return value anyway, but nonetheless I think you should add it to the clobber list. What if the optimizer decides to inline your function?

Also note that to be compatible to the "new" gcc 6.2, all registers in inline assembly have to be prefixed by %%.

mfro wrote:Make sure you call it in supervisor mode on anything else than a plain 68000. MOVE TO/FROM SR are privileged instructions.


Even on a plain 68000, MOVE TO SR is privileged and the code must be run from supervisor mode even there.
Last edited by czietz on Sun Feb 05, 2017 3:47 pm, edited 1 time in total.

czietz
Hardware Guru
Hardware Guru
Posts: 467
Joined: Tue May 24, 2016 6:47 pm

Re: Interrupts disable and reenable with GCC

Postby czietz » Sun Feb 05, 2017 3:46 pm

Posted this again, instead of editing previous post. Sorry!

User avatar
mfro
Atari Super Hero
Atari Super Hero
Posts: 674
Joined: Thu Aug 02, 2012 10:33 am
Location: SW Germany

Re: Interrupts disable and reenable with GCC

Postby mfro » Sun Feb 05, 2017 3:55 pm

Thank you. Fixed above.

User avatar
simonsunnyboy
Moderator
Moderator
Posts: 4849
Joined: Wed Oct 23, 2002 4:36 pm
Location: Friedrichshafen, Germany
Contact:

Re: Interrupts disable and reenable with GCC

Postby simonsunnyboy » Mon Feb 06, 2017 5:07 pm

Thanks for the complete but large solution.

I'll revert to the more simplistic approach.

What is necessary for a single line assembly statement? I'll probably just do what I did in the past in assembly:

Code: Select all

move    #$2700,SR       ; stop irq system
...
move    #$2300,SR       ; reenable irqs


My code is game/demo stuff so I'm basically always in supervisor mode.
Simon Sunnyboy/Paradize - http://paradize.atari.org/ - STOT: http://www.npoi.de/stot/

Stay cool, stay Atari!

1x2600jr, 1x1040STFm, 1x1040STE 4MB+TOS2.06+SatanDisk, 1xF030 14MB+FPU+NetUS-Bee

Jabber: simonsunnyboy@atari-jabber.org

User avatar
dml
Fuji Shaped Bastard
Fuji Shaped Bastard
Posts: 3472
Joined: Sat Jun 30, 2012 9:33 am

Re: Interrupts disable and reenable with GCC

Postby dml » Mon Feb 06, 2017 5:22 pm

It's the same, but you remove the unwanted code, and remove the unwanted constraints. I recommend you keep the "cc" clobber constraint though (means = condition codes modified).

Code: Select all

__asm__ __volatile__
(
 "move.w #0x2700,%%sr;"
 :  :  : "cc"
);


(edited to include the %% register prefix for gcc 6.2)

User avatar
simonsunnyboy
Moderator
Moderator
Posts: 4849
Joined: Wed Oct 23, 2002 4:36 pm
Location: Friedrichshafen, Germany
Contact:

Re: Interrupts disable and reenable with GCC

Postby simonsunnyboy » Mon Feb 06, 2017 6:58 pm

I'll use that! Thanks alot :)
Simon Sunnyboy/Paradize - http://paradize.atari.org/ - STOT: http://www.npoi.de/stot/

Stay cool, stay Atari!

1x2600jr, 1x1040STFm, 1x1040STE 4MB+TOS2.06+SatanDisk, 1xF030 14MB+FPU+NetUS-Bee

Jabber: simonsunnyboy@atari-jabber.org


Social Media

     

Return to “C / PASCAL etc.”

Who is online

Users browsing this forum: No registered users and 1 guest