When I read the ps/2 configuration byte, the system flag is not set even though it should be?
Here's how I read it:
char buffer;
asm volatile("inb $0x64, %%al; mov %%al, %0" : "=r" (buffer) : : "rax");
asm ("movb $0x20, %al; outb %al, $0x64");
asm volatile("inb $0x60, %%al; mov %%al, %0" : "=r" (buffer) :: "rax");
And the value of the buffer is: 01100001
The 2nd bit is the system flag and it is clearly not set (unless ps/2 is big endian?)
OSDEV wiki literally says that if the system flag is not set, your OS shouldnt be running
Edit: adding that the code was run under QEMU, as comments are not always permanent.
It turns out that the answer to your question is that the system flag isn't set in the configuration byte because QEMU doesn't ever set it.
Reading the byte under QEMU returns 0x61/01100001 (bit 2, the system flag, is not set). However, running the same code under VirtualBox, which appears to perform (or at least simulate) POST, the value is 0x45/01000101 (bit 5 is cleared to indicate a second PS/2 port).
The relevant code is in the QEMU source tree in hw/input/pckbd.c. It's a little hard to align with the OSDev Wiki, because the naming and descriptions aren't always consistent - in particular, the first and second PS/2 ports are specifically referred to as 'keyboard' and 'mouse'. (If my memory is correct here, the keyboard always had to be on the first port, because the mouse used extra pin signals the keyboard didn't).
What OSDev calls the Controller Configuration Byte, QEMU calls the Controller Mode Register, and is stored in a byte called mode. The flags are indicated by a series of #define values:
/* Controller Mode Register Bits */
#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */
#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */
#define KBD_MODE_SYS 0x04 /* The system flag (?) */
#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */
#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */
#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */
#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */
#define KBD_MODE_RFU 0x80
There's definitely some inconsistency with OSDev, including bit 3, which OSDev states "Should be zero", but QEMU seems to indicate that it can be set:
#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */
except that KBD_MODE_NO_KEYLOCK isn't used anywhere.
The particular #define that refers to the system flag:
#define KBD_MODE_SYS 0x04 /* The system flag (?) */
is never used either, and isn't part of the default initialized value of mode. Based on the comment, they don't even seem to know what it is.
Related
I'm fairly new to OS development and I recently started a hobby project of creating a simple-as-possible text-only operating system. It's written in C with some help from assembly and uses GRUB for booting, and I've been testing it in VirtualBox and also occasionally putting it on a flash drive for testing on an ancient (~2009) laptop. So far I've implemented some basic text output functions, and I think my GDT and IDT implementations are okay given the lack of crashing lately. Currently I'm trying to get an interrupt-driven keyboard driver working.
I think I've got the PICs set up correctly, and it seems I've had luck in giving commands to the PS/2 controller and keyboard and capturing responses via an interrupt handler. For example, here's the debug output when giving the keyboard an identify command:
Initializing kernel...
Setting PS/2 controller status: 0x05
Sending keyboard command: 0xF2
Keyboard interrupt: 0xFA
Keyboard interrupt: 0xAB
Keyboard interrupt: 0x83
The data returned seems to be correct, and this proves that my interrupt handler is able to work multiple times in succession without crashing or anything, so I'm not too worried about my IDT or ISR implementation. Now here's the output when I send the 0xF4 command to the keyboard to start scanning for key presses:
Initializing kernel...
Setting PS/2 controller status: 0x05
Sending keyboard command: 0xF4
Keyboard interrupt: 0xFA
The interrupt with the "acknowledge" status code 0xFA seems promising, but afterwards nothing happens when I press keys. For both examples, I got the same results when running both in VirtualBox and on the laptop I've been using.
Here's some relevant code from the keyboard driver:
#define KEYBD_DATA 0x60
#define KEYBD_CMD 0x64
// wrapper for interrupt service routine written in assembly
extern void keyboard_interrupt();
// called from assembly ISR
void keyboard_handler() {
u8 data = read_port(KEYBD_DATA);
print("Keyboard interrupt: 0x");
printx(data);
putc('\n');
pic_eoi();
}
// functions to print command before sending it to the port
void keyboard_command(u8 cmd) {
print("Sending keyboard command: 0x");
printx(cmd);
putc('\n');
write_port(KEYBD_DATA, cmd);
}
void controller_command(u8 cmd) {
print("Sending controller command: 0x");
printx(cmd);
putc('\n');
write_port(KEYBD_CMD, cmd);
}
void setup_keyboard() {
// flush keyboard output
while(read_port(KEYBD_CMD) & 1)
read_port(KEYBD_DATA);
// set interrupt descriptor table entry (default code segment and access flags)
set_idt_entry(0x21, &keyboard_interrupt);
// activate device
write_port(KEYBD_CMD, 0xAE);
wait();
// get status
write_port(KEYBD_CMD, 0x20);
wait();
u8 status = (read_port(KEYBD_DATA) | 1) & 0x05;
print("Setting PS/2 controller status: 0x");
printx(status);
putc('\n');
wait();
// set status
write_port(KEYBD_CMD, 0x60);
wait();
write_port(KEYBD_DATA, status);
wait();
// enable keyboard scanning
keyboard_command(0xf4);
}
Not that I think it's the root of the problem, but here's the assembly part of the interrupt handler just in case (in GNU assembly):
.extern keyboard_handler
.global keyboard_interrupt
keyboard_interrupt:
cli
pusha
cld
call keyboard_handler
popa
sti
iret
Here's the code that sets up the PICs beforehand:
#define MASTER_CMD 0x20
#define MASTER_DATA 0x21
#define SLAVE_CMD 0xA0
#define SLAVE_DATA 0xA1
#define PIC_EOI 0x20
// hopefully this gives a long enough delay
void wait() {
for (u8 i = 0; i < 255; i++);
}
// alert the PICs that the interrupt handling is done
// (later I'll check whether the slave PIC needs to be sent the EOI, but for now it doesn't seem to hurt to give it anyway)
void pic_eoi() {
write_port(MASTER_CMD, PIC_EOI);
write_port(SLAVE_CMD, PIC_EOI);
wait();
}
void setup_pic() {
write_port(MASTER_CMD, 0x11);
write_port(SLAVE_CMD, 0x11);
wait();
write_port(MASTER_DATA, 0x20);
write_port(SLAVE_DATA, 0x28);
wait();
write_port(MASTER_DATA, 0x4);
write_port(SLAVE_DATA, 0x2);
wait();
write_port(MASTER_DATA, 0x1);
write_port(SLAVE_DATA, 0x1);
wait();
write_port(MASTER_DATA, 0x0);
write_port(SLAVE_DATA, 0x0);
wait();
}
Here's the order of initializations in the main part of the kernel:
// initialize global descriptor table and interrupt descriptor table
setup_gdt();
setup_idt();
// setup hardware interrupts
setup_pic();
setup_keyboard();
activate_idt(); // assembly routine with lidt and sti
I also know that the keyboard is in fact doing its thing and putting scan codes on port 0x60, and I've been able to get a polling method of getting keypresses working, but it's messy and it would make it much harder to handle things like key repetition and keeping track of the shift key. Let me know if more code is needed. Hopefully there's just something obvious I'm either forgetting or doing wrong :)
General reasons why a specific IRQ, some IRQs, or all IRQs may not appear to work:
You haven't enabled interrupts on the CPU with sti (or equivalent)
You haven't enabled the interrupts(s) with a mask sent to the master and slave PICs when you initialise them.
Not properly acknowledging an EOI when an interrupt does occur can disable some or all interrupts depending on the priority of the interrupt.
You have disabled the PICs
You won't get a keyboard interrupt from the PS/2 keyboard unless you have sent a PS/2 controller configuration byte with bit 0 set (bit 1 is interrupt for the mouse)
I'd narrow down the problem space by masking off all external interrupts except the one you are testing. In your case you are interested in IRQ1. To mask off all external interrupts except IRQ1 you can change setup_pic so that:
write_port(MASTER_DATA, 0x0);
write_port(SLAVE_DATA, 0x0);
Becomes:
write_port(MASTER_DATA, ~0x2);
write_port(SLAVE_DATA, ~0x0);
Bits that are set mask off an interrupt and ones that are zero enable them. ~0x2 is the bitmask 0b11111101 and ~0x0 is the bitmask 0b11111111. That should disable all but IRQ1 (bit 1 of master PIC).
You discovered that the problem disappeared by using the suggestion above and then mention your default interrupt handler just does an IRET. You need to send a proper EOI even in your default do nothing IRQ handlers. Don't send EOIs for interrupts unless they come from the PICs. In your case IDT entry 0x20 to 0x2f (inclusive) need to have handlers that send proper EOIs. More detailed information on properly handling EOIs can be found on the OSDev Wiki
I'd guess what is going on is that on the first timer interrupt (IRQ0) you send no EOI, and that would effectively disable all external interrupts. Until an EOI is sent all external interrupts of equal or lower priority will be disabled. IRQ0 (timer) is the highest priority, so not sending an EOI effectively disables all external interrupts until an EOI is sent.
I'm trying to write an operating system using OSDev and others. Now, I'm stuck making a keyboard interrupt handler. When I compile my OS and run the kernel using qemu-system-i386 -kernel kernel/myos.kernel everything works perfectly. When I put everything into an ISO image and try to run it using qemu-system-i386 -cdrom myos.iso, it restarts when I press a key. I think it's caused by some problems in my interrupt handler or a bad IDT entry.
My keyboard handler (AT&T syntax):
.globl keyboard_handler
.align 4
keyboard_handler:
pushal
cld
call keyboard_handler_main
popal
iret
My main handler in C:
void keyboard_handler_main(void) {
unsigned char status;
char keycode;
/* write EOI */
write_port(0x20, 0x20);
status = read_port(KEYBOARD_STATUS_PORT);
/* Lowest bit of status will be set if buffer is not empty */
if (status & 0x01) {
keycode = read_port(KEYBOARD_DATA_PORT);
if(keycode < 0)
return;
if(keycode == ENTER_KEY_CODE) {
printf("\n");
return;
}
printf("%c", keyboard_map[(unsigned char) keycode]);
}
}
C function I use to load the:
void idt_init(void)
{
//unsigned long keyboard_address;
unsigned long idt_address;
unsigned long idt_ptr[2];
auto keyboard_address = (*keyboard_handler);
IDT[0x21].offset_lowerbits = keyboard_address & 0xffff;
IDT[0x21].selector = KERNEL_CODE_SEGMENT_OFFSET;
IDT[0x21].zero = 0;
IDT[0x21].type_attr = INTERRUPT_GATE;
IDT[0x21].offset_higherbits = (keyboard_address & 0xffff0000) >> 16;
/* Ports
* PIC1 PIC2
*Command 0x20 0xA0
*Data 0x21 0xA1
*/
write_port(0x20 , 0x11);
write_port(0xA0 , 0x11);
write_port(0x21 , 0x20);
write_port(0xA1 , 0x28);
write_port(0x21 , 0x00);
write_port(0xA1 , 0x00);
write_port(0x21 , 0x01);
write_port(0xA1 , 0x01);
write_port(0x21 , 0xff);
write_port(0xA1 , 0xff);
idt_address = (unsigned long)IDT ;
idt_ptr[0] = (sizeof (struct IDT_entry) * IDT_SIZE) + ((idt_address & 0xffff) << 16);
idt_ptr[1] = idt_address >> 16 ;
load_idt(idt_ptr);
printf("%s\n", "loadd");
}
Files are organized the same way as OSDev's Meaty Skeleton. I do have a different bootloader.
Based on experience I believed that this issue was related to a GDT not being set up. Often when someone says interrupts work with QEMU's -kernel option but not a real version of GRUB it is often related to the kernel developer not creating and loading their own GDT. The Mulitboot Specification says:
‘GDTR’
Even though the segment registers are set up as described above, the ‘GDTR’ may be invalid, so the OS image must not load any segment registers (even just reloading the same values!) until it sets up its own ‘GDT’.
When using QEMU with the -kernel option the GDTR is usually valid but it isn't guaranteed to be that way. When using a real version of GRUB (installed to a hard drive, virtual image, ISO etc) to boot you may discover that the GDTR isn't in fact valid. The first time you attempt to reload any segment register with a selector (even if it is the same value) it may fault. When using interrupts the code segment (CS) will be modified which may cause a triple fault and reboot.
As well the Multiboot Specification doesn't say which selectors point to code or data descriptors. Since the layout of the GDT entries is not known or guaranteed by the Multiboot specification it poses a problem for filling in the IDT entries. Each IDT entry needs to specify a specific selector that points to a code segment.
The Meaty Skeleton tutorial on OSDev doesn't set up interrupts, nor do they modify any of the segment registers so that code likely works with QEMU's -kernel option and a real version of GRUB. Adding IDT and interrupt code on top of the base tutorial probably lead to the failure you are seeing when booting with GRUB. That tutorial should probably make it clearer that you should set up your own GDT and not rely on the one set up by the Multiboot loader.
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.
I know dereferencing a null pointer is undefined - but I would like to know what happens on a specific target - an MSP430.
I don't have a board to load this on in front of me to test this out right now.
What would happen if I did this (or similar)?
int * foo = NULL;
(*foo)++; //Crash?
Location 0x0 is in the SFR range and is reserved.
Would it generate a PUC/POR? Or would it silently "work"?
The assembly generated is
;int * foo = NULL;
clr.w R15
;(*foo)++;
inc.w R15
So location 0x0 is literally being incremented by 1.
When I run this in the simulator I see the value at address 0x0 go from 0 to 1. I get no warnings in the debug log and the program exits normally.
I am using the IAR EW430 compiler/assembler/simulator.
Not only writing and reading the address 0x0 will not cause a crash or a reboot, it actually a completely legal operation that is often used by MSP430 applications.
The initial portion or MSP430 memory map is reserved for I/O ports and control registers: http://en.wikipedia.org/wiki/TI_MSP430#MSP430_address_space
In particular, the control registers at 0x0 and subsequent addresses are:
#define IE1_ 0x0000 /* Interrupt Enable 1 */
#define IE2_ 0x0001 /* Interrupt Enable 2 */
#define IFG1_ 0x0002 /* Interrupt Flag 1 */
#define IFG2_ 0x0003 /* Interrupt Flag 2 */
So for example writing zero to that memory address by dereferencing a uint8_t * or uint16_t * pointer is going to disable interrupts. Writing zero by dereferencing an uint32_t * it is also going to clear the flags. Incrementing the value of these registers does not make a lot of sense, but should be completely legal.
At least this is the case on msp430 Series 1, Series 2 and Series 4. By checking the header files I was not able to find anything mapped to 0x0 on Series 5 (the interrupt control registers are mapped to region starting from 0x0100).
So if you want to catch places in code where the NULL pointer is dereferenced, you're completely on your own.
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.