OK, now I'm confused.
Here is my understanding; show me where I'm wrong:
The CPU will check the interrupt pin at the end of its cycle.
If it's high, there is communication with the interrupt controller, an interrupt handler which is part of the OS is evoked, and communication with a driver takes place.
When the OS finally knows which device issued the hardware interrupt and acquires all of the information, it passes this data to some kind of "mailbox", an area in RAM where processes can pick up data left for them by the OS.
My question is: if I'm writing an application in assembly, I need to issue a software interrupt every X milliseconds and tell the kernel code I'm calling out to to check the mailbox location in RAM and tell me if data of the appropriate form is stored there.
What is that address?
The builder of the OS should have set it, not me the lowly application programmer.
Your explanation suggests that the application programmer somehow has control over that location, that he can just set it to whatever he wants.
But if my understanding of the entire scenario is correct, I don't see how the application programmer has any control over that at all.
With respect to the data, I was talking about the hardware interrupt.
The left button being pressed must map to some data expression e.g. 11001010.
Who decides this?
The driver? The interrupt handler?
Maybe it'll help if I explain using a somewhat more direct, lower level, less abstracted example. Let's say I have a program and I want to read directly from something that the kernel manages like a terminal, which maybe represents reading keyboard input or data off of a serial port. In Linux and other Unix-like OSes this communication is managed through files.
Here's a flow of what basically happens:
1) The user program sends the system call "open" with a filesystem location of where the device file is. For example:
Code:
int fd = open("/dev/ttyS0");
The specifics of what this means in assembly language depends on the details of the instruction set and how the OS works. It'll probably involve putting the arguments in registers and using some sort of system call instruction. Then the result will be returned in some register.
2) The kernel looks up the file path, sees that it's attached to a device driver, and hands over control to an open function registered with the device driver. The device driver does whatever initialization stuff is necessary for opening and returns to the kernel.
3) The kernel returns to the program a number identifying the open file.
4) The program sends a "read" syscall to the kernel to ask for data from the device, like so:
What this means is you want to read 4 characters from the device. Now what actually happens from here depends on the device and how it's configured. It might immediately return only the data it currently has (which could be nothing). Or it might stop and wait for data to arrive. I'm going to go with the later style (blocking access) because it seems more in line with the questions you were asking earlier.
5) The kernel sees that you want to read from the device opened with the descriptor in fd and calls the read function registered by the driver. The driver looks to see if it has 4 characters ready and it turns out it only has 3, so it tells the kernel to put the process to sleep.
6) The process yields control back to the kernel which can schedule another process (or do nothing if nothing is waiting to be run, in which case it probably puts the CPU in a low power state). The process is put in a sleeping state and won't be ran again until something changes that.
7) The user presses a key and an interrupt fires in the CPU. Control switches to the kernel's interrupt handler, which looks up the interrupt and finds that it's registered to the terminal driver, so it calls the driver's interrupt handler function.
8) The driver sees that the outstanding read syscall can now be completed. So it takes the data off of its buffer and puts it in the array the process told it to put it in with the read syscall. Then it tells the kernel to wake up the reading process, which puts it in the runnable state.
9) The kernel's scheduler runs the process again, which continues execution from where the read syscall left off.
You can see here that the kernel abstracts things like raw memory locations from the program, except where it's the program's managed memory (like the buffer to write the data to), in which case the program tells the kernel that location.
I don't really know the low level details of how something like X11 is implemented but it could work by having the window manager process read from some kernel devices that handle various I/O in a way much like what I described (maybe using non-blocking reads, maybe using different threads, etc). Then it could implement the event queues for different windows via something like sockets/named pipes which are files that are created process to process communication. Then the user application reads from the socket and just like with reading from a device, the OS will put the program to sleep if it's in blocking mode.