GameBoy compiler with system registers and interrupts - c

I have been spending a lot of time learning GameBoy programming, as I was already familiar with Z80 Assembly I wasn't afraid of jumping into using it. I would (of course) find it much more productive to program in C or C++ however cannot find a full compiler for the GameBoy, the compilers I can find manage everything themselves and do not give access to system registers to the programmer and also have some horrible drawbacks such as 100% CPU utilization and no interrupt support.
Would it be possible to address system registers much like Arduino's AVR compiler? being able to address a CPU or system register simply with its name such as DDRD = %10101011
What would I have to do to add interrupts and system registers into a compiler? All but one system register are only one byte memory addresses and interrupts vectors are of course memory locations, the only one system register that isn't a memory address can only be modified with two Assembly instructions EI and DI but that could be inline functions correct?

The usual tactic is to create your own pointers to system registers. I don't know the address of DDRD, but something like this should to the trick:
volatile unsigned char *reg_DDRD = (unsigned char *)0xE000;
*reg_DDRD = 0xAB;
Most C compilers don't support binary constants but you can use them with some macro hackery. And you can use macros to make slightly more intuitive syntax:
#define DDRD (*reg_DDRD)
DDRD = 0xAB;
There's no sense in modifying the compiler when vanilla C code can work just as well.
Handling interrupts comes down to solving 3 problems. The first is to have the interrupt vector address do a jump to a C function. As that is in ROM you'll need to modify the C runtime environment to initialize that. This gets pretty system dependent, but usually what you'll want to do is add an assembly language file that looks like this:
org 38h ; or wherever the gameboy CPU jumps to on interrupt
jp _intr_function
This should cause the CPU to go to intr_function() in your C program. You may or may not need the leading underscore. And you may not be able to set the destination address in the assembler file with org but instead have to fool around with the linker and sections.
The second problem is that the C function won't necessarily save all the registers it should. You can do this by adding in-line assembly to it, along these lines:
void intr_function()
{
asm(" push af");
asm(" push bc");
asm(" push de");
asm(" push hl");
// ... now do what you like here.
asm(" pop hl");
asm(" pop de");
asm(" pop bc");
asm(" pop af");
}
Finally, the interrupt may have to be acknowledged by manipulating a hardware register. But you can do that in the C code so nothing special about that.
I'm not clear about the issue with wait loops. Standard C compilers don't have such a feature built in. They call main() and if you want to loop it is up to you. It is true that the C-like language used in the Arduino SDK has its own built-in infinite loop that calls the functions you write, but that's not normal C.

First off, you can use GBDK, which is a C compiler and library for the Gameboy. It does provide access to the registers in gb/hardware.h (but that isn't listed in the doc file, since there's no comment for each individual register). It also supplies access to interrupts via methods in gb/gb.h: add_VBL, add_LCD, add_TIM, add_SIO, and add_JOY. (There's also remove methods named remove_).
For reference and/or your own use, here's the contents of gb/hardware.h:
#define __REG volatile UINT8 *
#define P1_REG (*(__REG)0xFF00) /* Joystick: 1.1.P15.P14.P13.P12.P11.P10 */
#define SB_REG (*(__REG)0xFF01) /* Serial IO data buffer */
#define SC_REG (*(__REG)0xFF02) /* Serial IO control register */
#define DIV_REG (*(__REG)0xFF04) /* Divider register */
#define TIMA_REG (*(__REG)0xFF05) /* Timer counter */
#define TMA_REG (*(__REG)0xFF06) /* Timer modulo */
#define TAC_REG (*(__REG)0xFF07) /* Timer control */
#define IF_REG (*(__REG)0xFF0F) /* Interrupt flags: 0.0.0.JOY.SIO.TIM.LCD.VBL */
#define NR10_REG (*(__REG)0xFF10) /* Sound register */
#define NR11_REG (*(__REG)0xFF11) /* Sound register */
#define NR12_REG (*(__REG)0xFF12) /* Sound register */
#define NR13_REG (*(__REG)0xFF13) /* Sound register */
#define NR14_REG (*(__REG)0xFF14) /* Sound register */
#define NR21_REG (*(__REG)0xFF16) /* Sound register */
#define NR22_REG (*(__REG)0xFF17) /* Sound register */
#define NR23_REG (*(__REG)0xFF18) /* Sound register */
#define NR24_REG (*(__REG)0xFF19) /* Sound register */
#define NR30_REG (*(__REG)0xFF1A) /* Sound register */
#define NR31_REG (*(__REG)0xFF1B) /* Sound register */
#define NR32_REG (*(__REG)0xFF1C) /* Sound register */
#define NR33_REG (*(__REG)0xFF1D) /* Sound register */
#define NR34_REG (*(__REG)0xFF1E) /* Sound register */
#define NR41_REG (*(__REG)0xFF20) /* Sound register */
#define NR42_REG (*(__REG)0xFF21) /* Sound register */
#define NR43_REG (*(__REG)0xFF22) /* Sound register */
#define NR44_REG (*(__REG)0xFF23) /* Sound register */
#define NR50_REG (*(__REG)0xFF24) /* Sound register */
#define NR51_REG (*(__REG)0xFF25) /* Sound register */
#define NR52_REG (*(__REG)0xFF26) /* Sound register */
#define LCDC_REG (*(__REG)0xFF40) /* LCD control */
#define STAT_REG (*(__REG)0xFF41) /* LCD status */
#define SCY_REG (*(__REG)0xFF42) /* Scroll Y */
#define SCX_REG (*(__REG)0xFF43) /* Scroll X */
#define LY_REG (*(__REG)0xFF44) /* LCDC Y-coordinate */
#define LYC_REG (*(__REG)0xFF45) /* LY compare */
#define DMA_REG (*(__REG)0xFF46) /* DMA transfer */
#define BGP_REG (*(__REG)0xFF47) /* BG palette data */
#define OBP0_REG (*(__REG)0xFF48) /* OBJ palette 0 data */
#define OBP1_REG (*(__REG)0xFF49) /* OBJ palette 1 data */
#define WY_REG (*(__REG)0xFF4A) /* Window Y coordinate */
#define WX_REG (*(__REG)0xFF4B) /* Window X coordinate */
#define KEY1_REG (*(__REG)0xFF4D) /* CPU speed */
#define VBK_REG (*(__REG)0xFF4F) /* VRAM bank */
#define HDMA1_REG (*(__REG)0xFF51) /* DMA control 1 */
#define HDMA2_REG (*(__REG)0xFF52) /* DMA control 2 */
#define HDMA3_REG (*(__REG)0xFF53) /* DMA control 3 */
#define HDMA4_REG (*(__REG)0xFF54) /* DMA control 4 */
#define HDMA5_REG (*(__REG)0xFF55) /* DMA control 5 */
#define RP_REG (*(__REG)0xFF56) /* IR port */
#define BCPS_REG (*(__REG)0xFF68) /* BG color palette specification */
#define BCPD_REG (*(__REG)0xFF69) /* BG color palette data */
#define OCPS_REG (*(__REG)0xFF6A) /* OBJ color palette specification */
#define OCPD_REG (*(__REG)0xFF6B) /* OBJ color palette data */
#define SVBK_REG (*(__REG)0xFF70) /* WRAM bank */
#define IE_REG (*(__REG)0xFFFF) /* Interrupt enable */
These are done in the same way as George Phillips's answer, and thus can be used like normal variables.
The code that is used by GBDK to add and remove interrupts is found in libc\gb\crt0.s, but I don't know enough of assembly to include the relevant sections in this post.
I'm not sure about how to avoid the busy loop either.

Related

Enable GPIO on AM335x in C

I have the following function initGPIO. The goal is to enable the GPIOs 0, 1 and 2 on beaglebone using am335x. How can I enable the corresponding GPIO set to the reg_GPIO that are given in the header file? I have the header file GPIO.h which contains the GPIO's numbers, register number and register structure. I have tried to set the GPIO in the function initGPIO. Does it make sense?
gpio.h
#include <stdint.h>
#include <stdbool.h>
#define GPIO0 0 /*!< GPIO 0 number */
#define GPIO1 1 /*!< GPIO 1 number */
#define GPIO2 2 /*!< GPIO 2 number */
#define GPIO3 3 /*!< GPIO 3 number */
// Base address for each gpio hardware module.
#define GPIO0_REG 0x44E07000 //<! gpio0 hardware module address.
#define GPIO1_REG 0x4804C000 //<! gpio1 hardware module address.
#define GPIO2_REG 0x481AC000 //<! gpio2 hardware module address.
#define GPIO3_REG 0x481AE000 //<! gpio3 hardware module address.
// Register Structure
typedef struct
{
volatile uint32_t irqstatus_set_0; // Offset 0x34 - Enables specific interrupt event to trigger.
volatile uint32_t irqstatus_set_1; // Offset 0x38 - Enables specific interrupt event to trigger.
volatile uint32_t irqwaken_0; // Offset 0x44 - Enables wakeup events on an interrupt.
volatile uint32_t irqwaken_1; // Offset 0x48 - Enables wakeup events on an interrupt.
volatile uint32_t ctrl; // Offset 0x130 - Controls clock gating functionality, i.e. enables module.
volatile uint32_t oe; // Offset 0x134 – Output Enable pin (clear bit to 0) output capability.
volatile uint32_t datain; // Offset 0x138 - Registers data read from the GPIO pins.
volatile uint32_t dataout; // Offset 0x13c - Sets value of GPIO output pins.
volatile uint32_t cleardataout; // Offset 0x190 - Clears to 0 bits in dataout
volatile uint32_t setdataout; // Offset 0x194 - Sets to 1 bits in dataout
} GPIO_REGS;
void initGPIO();
gpio.c
/*!
* \brief Initialize GPIOs.
*
* Enables GPIO0, GPIO1, and GPIO2 (GPIO3 not used in IDP. Also configures the output pins
* used in the IDP to control relays and address the ADC's on relays.
*
*****************************************************************************************/
void initGPIO()
{
//enable GPIOs
GPIO_REGS gpio_regs;
//might need to change ctrl
gpio_regs.datain |= (GPIO0 << GPIO0_REG );
gpio_regs.datain |= (GPIO1 << GPIO1_REG );
gpio_regs.datain |= (GPIO2 << GPIO2_REG );
}
By default Beaglebones come with Debian Linux. Unless you've decided to drop that, the kernel has a GPIO driver which assumes control of all GPIO. You should not try to access raw GPIO registers directly, but rather talk to the kernel driver. The simplest way to do that is to install libgpiod (which may be installed by default on recent Beagles) and call its API.
For bare bone platforms you may be used to do something like this to access hardware addresses:
volatile GPIO_REGS* gpio1_regs = (GPIO_REGS*)0x44E07000;
volatile GPIO_REGS* gpio2_regs = (GPIO_REGS*)0x4804C000;
Now your code isn't doing this either, but I'm going to assume that is what you meant.
Except inside an OS the memory offsets in you user-space application don't map one-on-one to hardware address offsets and this simply won't work.
If you want direct register access to things like this, you'll have to do something like Memory Mapping using /dev/mem or if your platform supports it, /dev/gpiomem (I know Raspberry Pi supports this, I'm not sure about the BBB). This will check if you are allowed to access the hardware address range you want, as well as make sure the address range is mapped correctly in your user-space.
For example:
/* open /dev/mem */
int fd = open("/dev/mem", O_RDWR|O_SYNC);
if (fd >= 0) {
/* mmap GPIO */
volatile GPIO_REGS* gpio1_regs = mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0x44E07000);
close(fd); /* No need to keep fd open after mmap */
/* Check for MAP_FAILED error */
if (gpio1_regs != (GPIO_REGS*)-1) {
/* Do whatever */
(The last argument to mmap is where you place your hardware address, I'm using 0x44E07000 in this example.)
For further reading, see also:
https://man7.org/linux/man-pages/man2/mmap.2.html
https://pagefault.blog/2017/03/14/access-hardware-from-userspace-with-mmap/

Reason for tampering of bitband mapping address at run time

I am using bit banding to set and clear a GPIO6[1] of LPC1857 using Keil.
The mapping address in bit band region to set GPIO6[1] comes 0x43EC4304
The mapping address in bit band region to clear GPIO6[1] comes 0x43EC5384
THE GPIO gets set successfully but does not get cleared.
When checked on debugger, it is observed that mapping address to clear the GPIO6[1] was getting set to 0x43EC5000. Thus the GPIO does not get clear.
Can someone please help understand why this is happening ?
And how can I resolve this mapping address setting to wrong address at run time ?
Following is code snippet:
/*In "main.c"*/
int main()
{
... //System initialization
... //GPIO init
...
Toggle_Peri();
}
/*In "Peri.c"*/
/* Bit band PERIPHERAL definitions */
#define BITBAND_PERI_REF 0x40000000 //Start Address of PeripheRALS
#define BITBAND_PERI_BASE 0x42000000 //Start Address of Peripheral Bit Band Alias Region
/* Basic bit band function definition */
#define BITBAND_PERI(a,b) ((BITBAND_PERI_BASE + ((a-BITBAND_PERI_REF)*(0x20)) + (b*(0x04)))) // Convert GPIO address
#define BITBAND_PERI_SetClearBit(a,b) ((*(volatile uint32_t *) (BITBAND_PERI(a,b))) = 1)
/*Note: Base Address of GPIO = 0x400F 4000*/
#define PERI_GPIO_ADDRESS_S 0x400F6218 //For GPIO6[1] set
#define PERI_GPIO_ADDRESS_C 0x400F629C //For GPIO6[1] clear
#define PERI_GPIO_BIT 1
...
...
...
...
void Toggle_Peri(void)
{
BITBAND_PERI_SetClearBit(PERI_GPIO_ADDRESS_S,PERI_GPIO_BIT); //Set the pin
Delay(0xFFFF);
BITBAND_PERI_SetClearBit(PERI_GPIO_ADDRESS_C,PERI_GPIO_BIT); //Clear the pin
Delay(0xFFFF);
}

U-boot sections specifications

I am currently trying to wrap my head around how u-boot is organized and I was wondering if you could help clarify some of the sections in an config -> include/configs/vexpress_aemv8a.h
#define CONFIG_SYS_TEXT_BASE 0x80000000
/* from what I understand, this is where the code thinks it will be placed in memory */
#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x7fff0)
/* I do not understand where this comes into play */
#define CPU_RELEASE_ADDR (CONFIG_SYS_SDRAM_BASE + 0x7fff0)
/* I do not understand where this comes into play */
#define V2M_BASE 0x80000000
/* I do not understand where this comes into play */
/* Miscellaneous configurable options */
#define CONFIG_SYS_LOAD_ADDR (V2M_BASE + 0x10000000)
/* I do not understand where this comes into play */
I would much appreciate if you could detail where and how these memory zones are used.

AVR32 UC3A0 how to utilize USART interrupt example and receive string then do something

I am having some trouble, and I cannot tell if it's my understanding of the Atmel syntax, the Atmel Studio 6.0 or, the program itself.
I cannot seem to get the interrupt handler to receive a simple string then do something. I have success with just implimenting a single character turning an LED when USART receives one character it turns the LED on, then if it receives a different character it turns the LED off. By the way I have a design board that the program is having some trouble getting to the receive sub routine because the send code within the main is so large, so it was suggested to me to utilize interrupts to fix this.
By the way, I am trialing this program on an EVK1100 AVR32 board MCU:AT32UC3A0512-U, not sure if any of you have played with these before, but they're pretty great. Not sure I like Atmel syntax though.
Anyway, you can see I'm doing nothing in the main at the moment until I get the receive portion working.
I'm fairly new to interrupts in the Atmel world.
Any help would be much appreciated. I made just a few modifications to the built in ASF USART interrup "INTC" project example.
Thanks,
#include <string.h>
#include <avr32/io.h>
#include "compiler.h"
#include "board.h"
#include "print_funcs.h"
#include "intc.h"
#if defined (__GNUC__)
# if defined (__AVR32_AP7000__)
# include "pm_at32ap7000.h"
# else
# include "power_clocks_lib.h"
# endif
#elif defined (__ICCAVR32__) || defined (__AAVR32__)
# if defined (__AT32AP7000__)
# include "pm_at32ap7000.h"
# else
# include "power_clocks_lib.h"
# endif
#endif
#include "gpio.h"
#include "usart.h"
//#include "conf_example.h"
# define EXAMPLE_TARGET_PBACLK_FREQ_HZ FOSC0 // PBA clock target frequency, in Hz
#if BOARD == EVK1100
# define EXAMPLE_USART (&AVR32_USART1)
# define EXAMPLE_USART_RX_PIN AVR32_USART1_RXD_0_0_PIN
# define EXAMPLE_USART_RX_FUNCTION AVR32_USART1_RXD_0_0_FUNCTION
# define EXAMPLE_USART_TX_PIN AVR32_USART1_TXD_0_0_PIN
# define EXAMPLE_USART_TX_FUNCTION AVR32_USART1_TXD_0_0_FUNCTION
# define EXAMPLE_USART_CLOCK_MASK AVR32_USART1_CLK_PBA
# define EXAMPLE_PDCA_CLOCK_HSB AVR32_PDCA_CLK_HSB
# define EXAMPLE_PDCA_CLOCK_PB AVR32_PDCA_CLK_PBA
# define EXAMPLE_USART_IRQ AVR32_USART1_IRQ
# define EXAMPLE_USART_BAUDRATE 57600
#endif
/**
* \brief The USART interrupt handler.
*
* \note The `__attribute__((__interrupt__))' (under GNU GCC for AVR32) and
* `__interrupt' (under IAR Embedded Workbench for Atmel AVR32) C function
* attributes are used to manage the `rete' instruction.
*/
#if defined (__GNUC__)
__attribute__((__interrupt__))
#elif defined(__ICCAVR32__)
__interrupt
#endif
static void usart_int_handler(void)
{
static char Cmnd[30];
int index = 0;
int c;
usart_read_char(EXAMPLE_USART, &c);
Cmnd[index++] = c;
if (c = '\r')
{
Cmnd[index] = '\0';
usart_write_line(EXAMPLE_USART, Cmnd);
}
}
/**
* \brief The main function.
*
* It sets up the USART module on EXAMPLE_USART. The terminal settings are 57600
* 8N1.
* Then it sets up the interrupt handler and waits for a USART interrupt to
* trigger.
*/
int main(void)
{
static const gpio_map_t USART_GPIO_MAP =
{
{EXAMPLE_USART_RX_PIN, EXAMPLE_USART_RX_FUNCTION},
{EXAMPLE_USART_TX_PIN, EXAMPLE_USART_TX_FUNCTION}
};
// USART options.
static const usart_options_t USART_OPTIONS =
{
.baudrate = 57600,
.charlength = 8,
.paritytype = USART_NO_PARITY,
.stopbits = USART_1_STOPBIT,
.channelmode = USART_NORMAL_CHMODE
};
#if BOARD == EVK1100 || BOARD == EVK1101 || BOARD == UC3C_EK \
|| BOARD == EVK1104 || BOARD == EVK1105 || BOARD == STK600_RCUC3L0 \
|| BOARD == STK600_RCUC3D
/*
* Configure Osc0 in crystal mode (i.e. use of an external crystal
* source, with frequency FOSC0) with an appropriate startup time then
* switch the main clock source to Osc0.
*/
pcl_switch_to_osc(PCL_OSC0, FOSC0, OSC0_STARTUP);
#elif BOARD == STK1000
pm_reset();
#elif BOARD == UC3L_EK
/*
* Note: on the AT32UC3L-EK board, there is no crystal/external clock
* connected to the OSC0 pinout XIN0/XOUT0. We shall then program the
* DFLL and switch the main clock source to the DFLL.
*/
pcl_configure_clocks(&pcl_dfll_freq_param);
/*
* Note: since it is dynamically computing the appropriate field values
* of the configuration registers from the parameters structure, this
* function is not optimal in terms of code size. For a code size
* optimal solution, it is better to create a new function from
* pcl_configure_clocks_dfll0() and modify it to use preprocessor
* computation from pre-defined target frequencies.
*/
#end if
// Assign GPIO to USART.
gpio_enable_module(USART_GPIO_MAP,
sizeof(USART_GPIO_MAP) / sizeof(USART_GPIO_MAP[0]));
// Initialize USART in RS232 mode.
usart_init_rs232(EXAMPLE_USART, &USART_OPTIONS,
EXAMPLE_TARGET_PBACLK_FREQ_HZ);
print(EXAMPLE_USART, ".: Using interrupts with the USART :.\r\n\r\n");
// Disable all interrupts.
Disable_global_interrupt();
// Initialize interrupt vectors.
INTC_init_interrupts();
/*
* Register the USART interrupt handler to the interrupt controller.
* usart_int_handler is the interrupt handler to register.
* EXAMPLE_USART_IRQ is the IRQ of the interrupt handler to register.
* AVR32_INTC_INT0 is the interrupt priority level to assign to the
* group of this IRQ.
*/
INTC_register_interrupt(&usart_int_handler, EXAMPLE_USART_IRQ, AVR32_INTC_INT0);
// Enable USART Rx interrupt.
EXAMPLE_USART->ier = AVR32_USART_IER_RXRDY_MASK;
print(EXAMPLE_USART, "Type a character to use the interrupt handler."
"\r\nIt will show up on your screen.\r\n\r\n");
// Enable all interrupts.
Enable_global_interrupt();
/**
* We have nothing left to do in the main, so we may switch to a device
* sleep mode: we just need to be sure that the USART module will be
* still be active in the chosen sleep mode. The sleep mode to use is
* the FROZEN sleep mode: in this mode the PB clocks are still active
* (so the USART module which is on the Peripheral Bus will still be
* active while the CPU and HSB will be stopped).
* --
* Modules communicating with external circuits should normally be
* disabled before entering a sleep mode that will stop the module
* operation: this is not the case for the FROZEN sleep mode.
* --
* When the USART interrupt occurs, this will wake the CPU up which will
* then execute the interrupt handler code then come back to the
* while(1) loop below to execute the sleep instruction again.
*/
while(1)
{
/*
* If there is a chance that any PB write operations are
* incomplete, the CPU should perform a read operation from any
* register on the PB bus before executing the sleep
* instruction.
*/
AVR32_INTC.ipr[0]; // Dummy read
/*
* When the device wakes up due to an interrupt, once the
* interrupt has been serviced, go back into FROZEN sleep mode.
*/
}
}
Try to keep your interrupt handlers short. Interrupt handlers or ISRs are executed usually with interrupts disabled. Their goal is to get in, do something, and get out quickly. If you have other interrupts running (like a real-time clock for example), they are blocked out while you're in the ISR and that can cause problems such as lost interrupts.
The best thing to do here would be to declare the input buffer as a global static. Also define a static uchar_t have_line or similar, and in the ISR, save each character until either the buffer is full (an important check!), or you receive a CR (\r). Also check for newline (\n). Save a zero to null-terminate the buffer, and then set have_line = 1.
In your main loop, wait for have_line to be non-zero, and then process what's in the input buffer. Finally, set have_line back to zero.
That way, your ISR is brief and fairly robust.
In your sample code, does the function usart_write_line require interrupts to be enabled?
Oops! Sorry, I just noticed that your index isn't a static variable. That means it'll be initialized to zero each time the routine is called. Add a static declarator in front of int index and that should sort it out.

How to disable/enable interrupts on a stm32f107 chip?

I have an ARM stm32f107 chip. I'm porting a project from IAR to GCC
IAR provides the following functions to enable and disable interrupts:
#define __disable_interrupt() ...
#define __enable_interrupt() ...
How do I enable / disable interrupt for my chip using GCC?
When developing for the STM32, RM0008 is your best friend. From Section 10.2.4 on page 199:
To generate the interrupt, the interrupt line should be configured and
enabled. This is done by programming the two trigger registers with
the desired edge detection and by enabling the interrupt request by
writing a ‘1’ to the corresponding bit in the interrupt mask register.
So you need to set the appropriate mask bits in the appropriate registers. For external interrupts, that's the EXTI_IMR and EXTI_EMR registers. There are many others.
I can't answer for ARM but the same function in Coldfire boils down to setting/clearing the Interrupt Priority Level masking register in the CPU. Setting it to the highest number disables/ignores all but non-maskable, setting it to 0 enables all (YMMV).
Worth noting that it's handy to read-back the value when "disabling" and restore when "enabling" to ensure that stacked interrupts don't break each other:
ipl = DisableInts(); // Remember what the IPL was
<"Risky" code happens here>
EnableInts(ipl); // Restore value
This is useful when twiddling interrupt masks, which may cause spurious interrupts, or doing stuff that shouldn't be interrupted.
Functions come out as:
uint8 DisableInts(void)
{
return(asm_set_ipl(7));
}
uint8 EnableInts(uint8 ipl)
{
return(asm_set_ipl(ipl));
}
Both of which map to this asm:
asm_set_ipl:
_asm_set_ipl:
/* Modified for CW7.2! */
link A6,#-8
movem.l D6-D7,(SP)
move.l D0,D6 /* save argument */
move.w SR,D7 /* current sr */
move.l D7,D0 /* prepare return value */
andi.l #0x0700,D0 /* mask out IPL */
lsr.l #8,D0 /* IPL */
andi.l #0x07,D6 /* least significant three bits */
lsl.l #8,D6 /* move over to make mask */
andi.l #0x0000F8FF,D7 /* zero out current IPL */
or.l D6,D7 /* place new IPL in sr */
move.w D7,SR
movem.l (SP),D6-D7
//lea 8(SP),SP
unlk A6
rts
The ARM Documentation says that _enable_irq(); compiles to “CPSIE I” that means Clear All Masks. On the other hand _disable_irq(); compiles to Set Mask.

Resources