Skip to content

Commit e9d0d6c

Browse files
committed
lesson 21, keyboard input and shell
1 parent 5aaabf1 commit e9d0d6c

File tree

25 files changed

+1217
-3
lines changed

25 files changed

+1217
-3
lines changed

20-interrupts-timer/Makefile

Lines changed: 0 additions & 1 deletion
This file was deleted.

20-interrupts-timer/Makefile

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
C_SOURCES = $(wildcard kernel/*.c drivers/*.c cpu/*.c libc/*.c)
2+
HEADERS = $(wildcard kernel/*.h drivers/*.h cpu/*.h libc/*.h)
3+
# Nice syntax for file extension replacement
4+
OBJ = ${C_SOURCES:.c=.o cpu/interrupt.o}
5+
6+
# Change this if your cross-compiler is somewhere else
7+
CC = /usr/local/i386elfgcc/bin/i386-elf-gcc
8+
GDB = /usr/local/i386elfgcc/bin/i386-elf-gdb
9+
# -g: Use debugging symbols in gcc
10+
CFLAGS = -g
11+
12+
# First rule is run by default
13+
os-image.bin: boot/bootsect.bin kernel.bin
14+
cat $^ > os-image.bin
15+
16+
# '--oformat binary' deletes all symbols as a collateral, so we don't need
17+
# to 'strip' them manually on this case
18+
kernel.bin: boot/kernel_entry.o ${OBJ}
19+
i386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary
20+
21+
# Used for debugging purposes
22+
kernel.elf: boot/kernel_entry.o ${OBJ}
23+
i386-elf-ld -o $@ -Ttext 0x1000 $^
24+
25+
run: os-image.bin
26+
qemu-system-i386 -fda os-image.bin
27+
28+
# Open the connection to qemu and load our kernel-object file with symbols
29+
debug: os-image.bin kernel.elf
30+
qemu-system-i386 -s -fda os-image.bin -d guest_errors,int &
31+
${GDB} -ex "target remote localhost:1234" -ex "symbol-file kernel.elf"
32+
33+
# Generic rules for wildcards
34+
# To make an object, always compile from its .c
35+
%.o: %.c ${HEADERS}
36+
${CC} ${CFLAGS} -ffreestanding -c $< -o $@
37+
38+
%.o: %.asm
39+
nasm $< -f elf -o $@
40+
41+
%.bin: %.asm
42+
nasm $< -f bin -o $@
43+
44+
clean:
45+
rm -rf *.bin *.dis *.o os-image.bin *.elf
46+
rm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o cpu/*.o

20-interrupts-timer/drivers/screen.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include "screen.h"
2-
#include "ports.h"
3-
#include "../kernel/util.h"
2+
#include "../drivers/ports.h"
43

54
/* Declaration of private functions */
65
int get_cursor_offset();

21-shell/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../20-interrupts-timer/Makefile

21-shell/README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
2+
**Goal: Clean the code a bit and parse user input**
3+
4+
In this lesson we will do tho things. First, we will clean up the code a bit, so it is ready
5+
for further lessons. During the previous ones I tried to put things in the most predictable places,
6+
but it is also a good exercise to know when the code base is growing and adapt it to current
7+
and further needs.
8+
9+
10+
Code cleaning
11+
-------------
12+
13+
First of all, we will quickly start to need more utility functions
14+
for handling strings and so on. In a regular OS, this is called the C library,
15+
or libc for short.
16+
17+
Right now we have a `utils.c` which we will split into `mem.c` and `string.c`, with their respective headers.
18+
19+
Second, we will create a new function `irq_install()` so that the kernel
20+
only needs to perform one call to initialize all the IRQs. That function
21+
is akin to `isr_install()` and placed on the same `irq.c`.
22+
While we're here, we will disable the `kprint()` on `timer_callback()`
23+
to avoid filling the screen with junk, now that we know that it works
24+
properly.
25+
26+
There is not a clear distinction between `cpu/` and `drivers/`.
27+
Keep in mind that I'm
28+
creating this tutorial while following many others, and each of them
29+
has a distinct folder structure. The only change we will do for now is to
30+
move `drivers/ports.*` into `cpu/` since it is clearly cpu-dependent code.
31+
`boot/` is also CPU-dependent code, but we will not mess with it until
32+
we implement the boot sequence for a different machine.
33+
34+
35+
Keyboard characters
36+
-------------------
37+
38+
How to access the typed characters, then?
39+
40+
- When a key is pressed, the callback gets the ASCII code via a new
41+
arrays which are defined at the beginning of `keyboard.c`
42+
- The callback then appends that character to a buffer, `key_buffer`
43+
- It is also printed on the screen
44+
- When the OS wants to read user input, it calls `libc/io.c:readline()`
45+
46+
`keyboard.c` also parses backspace, by removing the last element
47+
of the key buffer, and deleting it from the screen, by calling
48+
`screen.c:kprint_backspace()`. For this we needed to modify a bit
49+
`print_char()` to not advance the offset when printing a backspace
50+
51+
52+
Responding to user input
53+
------------------------
54+
55+
The keyboard callback checks for a newline, and then calls the kernel,
56+
telling it that the user has input something. Out final libc function
57+
is `strcmp()`, which compares two strings and returns 0 if they
58+
are equal. If the user inputs "END", we halt the CPU.
59+
60+
This is the most basic shell ever, but you should be proud, because
61+
we implemented it from scratch. Do you realize how cool this is?
62+
63+
If you want to, expand `kernel.c` to parse more stuff. In the future,
64+
when we have a filesystem, we will allow the user to run some basic commands.

21-shell/boot

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../20-interrupts-timer/boot

21-shell/cpu/idt.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include "idt.h"
2+
3+
void set_idt_gate(int n, u32 handler) {
4+
idt[n].low_offset = low_16(handler);
5+
idt[n].sel = KERNEL_CS;
6+
idt[n].always0 = 0;
7+
idt[n].flags = 0x8E;
8+
idt[n].high_offset = high_16(handler);
9+
}
10+
11+
void set_idt() {
12+
idt_reg.base = (u32) &idt;
13+
idt_reg.limit = IDT_ENTRIES * sizeof(idt_gate_t) - 1;
14+
/* Don't make the mistake of loading &idt -- always load &idt_reg */
15+
__asm__ __volatile__("lidtl (%0)" : : "r" (&idt_reg));
16+
}

21-shell/cpu/idt.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#ifndef IDT_H
2+
#define IDT_H
3+
4+
#include "types.h"
5+
6+
/* Segment selectors */
7+
#define KERNEL_CS 0x08
8+
9+
/* How every interrupt gate (handler) is defined */
10+
typedef struct {
11+
u16 low_offset; /* Lower 16 bits of handler function address */
12+
u16 sel; /* Kernel segment selector */
13+
u8 always0;
14+
/* First byte
15+
* Bit 7: "Interrupt is present"
16+
* Bits 6-5: Privilege level of caller (0=kernel..3=user)
17+
* Bit 4: Set to 0 for interrupt gates
18+
* Bits 3-0: bits 1110 = decimal 14 = "32 bit interrupt gate" */
19+
u8 flags;
20+
u16 high_offset; /* Higher 16 bits of handler function address */
21+
} __attribute__((packed)) idt_gate_t ;
22+
23+
/* A pointer to the array of interrupt handlers.
24+
* Assembly instruction 'lidt' will read it */
25+
typedef struct {
26+
u16 limit;
27+
u32 base;
28+
} __attribute__((packed)) idt_register_t;
29+
30+
#define IDT_ENTRIES 256
31+
idt_gate_t idt[IDT_ENTRIES];
32+
idt_register_t idt_reg;
33+
34+
35+
/* Functions implemented in idt.c */
36+
void set_idt_gate(int n, u32 handler);
37+
void set_idt();
38+
39+
#endif

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy