Notes on 30 days homemade operating system -- Day7

Posted by mgoerz on Sun, 30 Jan 2022 19:53:06 +0100

1. Get key code

There is little difference between mouse and keyboard in code implementation, so we can imitate the other as long as we complete one of them.
Because after pressing a key before, nothing else can be done.
Modify init inthandler21 function in C

#define PORT_KEYDAT 0x0060 / / the device number is specified by IBM. For details, please refer to http://community.osdev.info/?(AT)keyboard
void inthandler21(int *esp)
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
	unsigned char data, s[4];
	io_out8(PIC0_OCW2, 0x61); /* Notify pic that "irq-01 has been accepted". If it is IRQ3, it will be written as 0x63.
In other words, output "0x60+IRQ number" to OCW2 to execute this sentence. After that, PIC continues to monitor whether IRQ1 interrupt occurs at any time*/
	data = io_in8(PORT_KEYDAT);
	sprintf(s, "%02X", data);
	boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
	putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
	return;
}

2. Speed up interrupt handling

The disadvantage of the above program is that the content of the character display is placed in the interrupt handler. If the interruption speed of the keyboard is too slow, the movement of the mouse will be incoherent, and the data cannot be received from the Internet. So we can first save the key code to the variable, and then check the variable occasionally by the main function. If there is, it will be displayed.
Therefore, the function before modification is as follows:

struct KEYBUF {
	unsigned char data, flag;/*flag Variable to indicate whether the buffer is empty*/
};
#define PORT_KEYDAT 0x0060
struct KEYBUF keybuf;
void inthandler21(int *esp)
{
	unsigned char data;
	io_out8(PIC0_OCW2, 0x61); /* Inform pic that irq-01 has been accepted */
	data = io_in8(PORT_KEYDAT);
if (keybuf.flag == 0) {
	keybuf.data = data;
	keybuf.flag = 1;
}
return;
}

For the main function:

for (;;) {
	io_cli();//Mask interrupt first
	if (keybuf.flag == 0) {
	io_stihlt();//STI and HLT cannot be written separately_ sti(); io_ hlt(); Overcome the interruption between two instructions
} else {
	i = keybuf.data;
	keybuf.flag = 0;
	io_sti();//Open interrupt
	sprintf(s, "%02X", i);
	boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
	putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
}
}

3. Make FIFO buffer

The program written by the former can only store one byte. Next, we are going to make a buffer for storing multiple bytes, so that it will not be full immediately.
The simplest is to add the data variable in the KEYBUF structure (the result is that the program becomes lengthy)

//Method 1:
struct KEYBUF {
unsigned char data1, data2, data3, data4, ...
};
//Method 2:
struct KEYBUF {
unsigned char data[4];
};
//After making FIFO buffer improvement:
struct KEYBUF {
unsigned char data[32];
int next;
};
void inthandler21(int *esp)
{
unsigned char data;
io_out8(PIC0_OCW2, 0x61); /* Inform pic that irq-01 has been accepted */
data = io_in8(PORT_KEYDAT);
if (keybuf.next < 32) {
keybuf.data[keybuf.next] = data;
keybuf.next++;
}
return;
}

4. Improve FIFO buffer

The goal of this section is to develop a FIFO type buffer that does not require data transfer operations. The general process is to read and write at the same time. When the reading is consistent with the writing, it indicates that the buffer is empty. When it reaches the end of the data, it is set to zero and start over.

//Modify KEYBUF
struct KEYBUF {
	unsigned char data[32];
	int next_r, next_w, len;
};
void inthandler21(int *esp)
{
	unsigned char data;
	io_out8(PIC0_OCW2, 0x61); /* Notify IRQ-01 that the acceptance has been completed */
	data = io_in8(PORT_KEYDAT);
	if (keybuf.len < 32) {
		keybuf.data[keybuf.next_w] = data;
		keybuf.len++;
		keybuf.next_w++;
		if (keybuf.next_w == 32) {
			keybuf.next_w = 0;
			}
	}
	return;
}
//Fetch data
for (;;) {
io_cli();
if (keybuf.len == 0) {
io_stihlt();
} else {
i = keybuf.data[keybuf.next_r];
keybuf.len--;
keybuf.next_r++;
if (keybuf.next_r == 32) {
keybuf.next_r = 0;
}
io_sti();
sprintf(s, "%02X", i);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
}
}

5. Organize FIFO buffer

Because each time the mouse moves, it will send 3 bytes of data continuously. Now define the buffer as variable.

struct FIFO8 {
	unsigned char *buf;//Storage buffer address
	int p, q, size, free, flags;
	//p: Next data write address (next_w)
	//q: Next data read address (next+r)
	//size: save the total number of words
	//free: the number of bytes in which there is no data in the save buffer
};
/*************FIFO*********************/
/*******Initialize FIFO buffer***************/
void fifo8_init(struct FIFO8 *fifo, int size, unsigned char *buf)
{
	fifo->size = size;
	fifo->buf = buf;
	fifo->free = size; /* Buffer size */
	fifo->flags = 0;
	fifo->p = 0; /* Next data write location */
	fifo->q = 0; /* Next data readout position */
	return;
}
/**Function to store 1-byte information to FIFO buffer*******/
#define FLAGS_OVERRUN 0x0001
int fifo8_put(struct FIFO8 *fifo, unsigned char data)
{
	if (fifo->free == 0) {
	/* There is no spare time, overflow */
		fifo->flags |= FLAGS_OVERRUN;//Whether the record overflows
		return -1;//Overflow
		}
	fifo->buf[fifo->p] = data;
	fifo->p++;
	if (fifo->p == fifo->size) {
		fifo->p = 0;
		}
	fifo->free--;
	return 0;//No overflow
}
/**Function to obtain 1-byte information from FIFO buffer*******/
int fifo8_get(struct FIFO8 *fifo)
{
	int data;
	if (fifo->free == fifo->size) {
		return -1;/* If the buffer is empty, - 1 is returned */
	}
	data = fifo->buf[fifo->q];
	fifo->q++;
	if (fifo->q == fifo->size) {
		fifo->q = 0;
	}
	fifo->free++;
	return data;
/**Report how much data you have accumulated*****/
int fifo8_status(struct FIFO8 *fifo)
{
	return fifo->size - fifo->free;
}

6. Finally talking about the mouse

Mouse interrupt number: IRQ12
To make the mouse interrupt effective, the following two devices need to be effective:
① The mouse control circuit is valid
② The mouse itself is valid

#define KEYCMD_SENDTO_MOUSE 0xd4
#define MOUSECMD_ENABLE 0xf4
void enable_mouse(void)
{
	/* Activate mouse */
	wait_KBC_sendready();//Make the keyboard control circuit ready for action and wait for the arrival of control instructions
	io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);
	wait_KBC_sendready();
	io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);
	return; /* If successful, the keyboard control will return ACK(0xfa)*/
}

7. Receive data from the mouse

After the interrupt is completed, the data will be taken out from the interrupt. (similar to keyboard)

struct FIFO8 mousefifo;
void inthandler2c(int *esp)
/* Interrupt from PS/2 mouse */
{
	unsigned char data;
	io_out8(PIC1_OCW2, 0x64); /* (Inform pic1 (from) that the acceptance of irq-12 has been completed */
	io_out8(PIC0_OCW2, 0x62); /* ((Master) inform pic0 that the acceptance of irq-02 has been completed */
	data = io_in8(PORT_KEYDAT);
	fifo8_put(&mousefifo, data);
	return;
}
/************************/
fifo8_init(&mousefifo, 128, mousebuf);
for (;;) {
	io_cli();
	if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) {
			io_stihlt();
		} else {
		if (fifo8_status(&keyfifo) != 0) {
		i = fifo8_get(&keyfifo);
		io_sti();
		sprintf(s, "%02X", i);
		boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
		putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
		} else if (fifo8_status(&mousefifo) != 0) {
			i = fifo8_get(&mousefifo);
			io_sti();
			sprintf(s, "%02X", i);
			boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 47, 31);
			putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
		}
	}
}

ok, I've finished the terminal operation of keyboard and mouse today, and I'll continue to shout tomorrow.

Topics: C Embedded system Operating System