Floppy disk controller, new keyboard and PIT driver interface, many things

- Implemented basic FDC capable of reading floppy disc sectors
 - Now PIT timer can be used, as it counts the ticks
 - Now the keyboard is buffered, instead of being accessed only during the interrupt itself
 - HAL for sleep()
 - gets()
 - atoi()
 - Now shell is not called by the IRQ, but it's a procedure that reads the key buffer
This commit is contained in:
Arnau Camprubí 2022-08-15 01:34:15 +02:00
pare 109b5b17a1
commit be55b8121c
S'han modificat 19 arxius amb 497 adicions i 47 eliminacions

240
src/kernel/arch/i686/fdc.c Normal file
Veure arxiu

@ -0,0 +1,240 @@
#include <stdbool.h>
#include "fdc.h"
#include "isr.h"
#include "io.h"
#include "../../lib/time.h"
static volatile bool g_fdc_irq = false;
static int g_curr_drive = 0;
static void i686_fdc_irq(registers *regs){
g_fdc_irq = true;
}
static inline void wait_irq(){
while(!g_fdc_irq); // Wait until g_fdc_irq is true
g_fdc_irq = false; // Then reset it to false
}
void i686_fdc_initialize_dma(){
i686_outb(0x0A, 0x06); // Mask DMA channel 2
i686_outb(0xD8, 0xFF); // Reset master flip-flop
i686_outw(0x04, 0x1000); // Address = 0x1000
i686_outb(0xD8, 0xFF); // Reset master flip-flop
i686_outb(0x05, 0xFF); // Count to 0x23FF (bytes in 2.5" floppy disk track)
i686_outb(0x05, 0x23);
i686_outb(0x80, 0x00); // External page register = 0
i686_outb(0x0A, 0x02); // Unmask DMA channel 2
}
void i686_fdc_dma_read(){
i686_outb(0x0A, 0x06); // Mask DMA channel 2
i686_outb(0x0B, 0x56); // single transfer, address increment, autoinit, read, channel 2
i686_outb(0x0A, 0x02); // Unmask DMA channel 2
}
void i686_fdc_dma_write(){
i686_outb(0x0A, 0x06); // Mask DMA channel 2
i686_outb(0x0B, 0x5A); // single transfer, address increment, autoinit, write, channel 2
i686_outb(0x0A, 0x02); // Unmask DMA channel 2
}
uint8_t i686_fdc_msr_read(){
return i686_inb(i686_FDC_MSR);
}
void i686_fdc_send_cmd(uint8_t cmd){
while(!(i686_fdc_msr_read() & i686_FDC_MSR_DATAREG));
i686_outb(i686_FDC_FIFO, cmd);
}
uint8_t i686_fdc_read_data(){
while(!(i686_fdc_msr_read() & i686_FDC_MSR_DATAREG));
return i686_inb(i686_FDC_FIFO);
}
void i686_fdc_write_ccr(uint8_t v){
i686_outb(i686_FDC_CTRL, v);
}
void i686_fdc_drive_data(uint32_t stepr, uint32_t loadt, uint32_t unloadt, bool dma){
uint32_t data = 0;
i686_fdc_send_cmd(i686_FDC_CMD_SPECIFY);
data = ((stepr & 0xF) << 4) | (unloadt & 0xF);
i686_fdc_send_cmd(data);
data = loadt << 1 | dma ? 1 : 0; // Ensure dma is a 0/1 bool
i686_fdc_send_cmd(data);
}
void i686_fdc_check_int(uint32_t *st0, uint32_t *cyl){
i686_fdc_send_cmd(i686_FDC_CMD_CHECK_INT);
*st0 = i686_fdc_read_data();
*cyl = i686_fdc_read_data();
}
int i686_fdc_motor(bool b){
if(g_curr_drive > 3) return -2;
uint32_t motor = 0;
switch(g_curr_drive){
case 0: motor = i686_FDC_DOR_DRIVE0_MOTOR; break;
case 1: motor = i686_FDC_DOR_DRIVE1_MOTOR; break;
case 2: motor = i686_FDC_DOR_DRIVE2_MOTOR; break;
case 3: motor = i686_FDC_DOR_DRIVE3_MOTOR; break;
}
if(b) i686_outb(i686_FDC_DOR, g_curr_drive | motor | i686_FDC_DOR_RESET | i686_FDC_DOR_DMA);
else i686_outb(i686_FDC_DOR, i686_FDC_DOR_RESET);
// Give the motor some time
sleep(20);
}
void i686_fdc_read_sector_imp(uint8_t head, uint8_t track, uint8_t sector){
uint32_t st0, cyl;
// Set DMA for read
i686_fdc_dma_read();
// Read a sector
i686_fdc_send_cmd(i686_FDC_CMD_READ_SECT | i686_FDC_CMD_EXT_MULTITRACK | i686_FDC_CMD_EXT_SKIP | i686_FDC_CMD_EXT_DENSITY);
i686_fdc_send_cmd(head << 2 | g_curr_drive);
i686_fdc_send_cmd(track);
i686_fdc_send_cmd(head);
i686_fdc_send_cmd(sector);
i686_fdc_send_cmd(i686_FDC_SECTOR_DTL_512);
i686_fdc_send_cmd((sector + 1) >= i686_FDC_SECTORS_PER_TRACK ? i686_FDC_SECTORS_PER_TRACK : sector + 1);
i686_fdc_send_cmd(i686_FDC_GAP3_3_5);
i686_fdc_send_cmd(0xFF);
// Wait for IRQ
wait_irq();
// Read output
for(int i = 0; i < 7; i++) i686_fdc_read_data();
// Tell the FDC we are done
i686_fdc_check_int(&st0, &cyl);
}
static void lba_to_chs(int lba, int *head, int *track, int *sector){
*head = (lba % (i686_FDC_SECTORS_PER_TRACK * 2)) / i686_FDC_SECTORS_PER_TRACK;
*track = lba / (i686_FDC_SECTORS_PER_TRACK * 2);
*sector = lba % i686_FDC_SECTORS_PER_TRACK + 1;
}
uint8_t *i686_fdc_read_sector(int lba){
if(g_curr_drive > 3) return 0;
// Convert LBA to CHS
int head = 0, track = 0, sector = 1;
lba_to_chs(lba, &head, &track, &sector);
// Turn on motor and seek
i686_fdc_motor(true);
if(i686_fdc_seek(track, head) != 0) return 0;
// Read and turn off motor
i686_fdc_read_sector_imp(head, track, sector);
i686_fdc_motor(false);
return (uint8_t *)0x1000;
}
int i686_fdc_calibrate(uint32_t drive){
uint32_t st0, cyl;
if(drive > 3) return -2;
i686_fdc_motor(true);
for(int i = 0; i < 10; i++){
// Send command
i686_fdc_send_cmd(i686_FDC_CMD_CALIBRATE);
i686_fdc_send_cmd(drive);
wait_irq();
i686_fdc_check_int(&st0, &cyl);
if(!cyl){
i686_fdc_motor(false);
return 0;
}
}
i686_fdc_motor(false);
return -1;
}
int i686_fdc_seek(uint32_t cyl, uint32_t head){
uint32_t st0, cyl0;
if(g_curr_drive > 3) return -2;
for(int i = 0; i < 10; i++){
// Send the command
i686_fdc_send_cmd(i686_FDC_CMD_SEEK);
i686_fdc_send_cmd(head << 2 | g_curr_drive);
i686_fdc_send_cmd(cyl);
// Wait for the results
wait_irq();
i686_fdc_check_int(&st0, &cyl0);
// Found?
if(cyl0 == cyl) return 0;
}
return -1;
}
void i686_fdc_disable(){
i686_outb(i686_FDC_DOR, 0);
}
void i686_fdc_enable(){
i686_outb(i686_FDC_DOR, i686_FDC_DOR_RESET | i686_FDC_DOR_DMA);
}
void i686_fdc_reset(){
uint32_t st0, cyl;
// Reset the controller
i686_fdc_disable();
i686_fdc_enable();
wait_irq();
// CHECK_INT to all drives
for(int i = 0; i < 4; i++) i686_fdc_check_int(&st0, &cyl);
// 500kb/s
i686_fdc_write_ccr(0);
// steprate = 3ms, unload time = 240ms, load time = 16ms, dma = true
i686_fdc_drive_data(3, 16, 240, true);
// Calibrate the disk
i686_fdc_calibrate(g_curr_drive);
}
void i686_fdc_set_working_drive(int drive){
if(drive < 4) g_curr_drive = drive;
}
void i686_fdc_initialize(){
debug_printf("[FDC] Initializing\n");
// Setup IRQ
i686_isr_register_handler(IRQ(6), i686_fdc_irq);
// Initialize DMA
i686_fdc_initialize_dma();
// Reset FDC
i686_fdc_reset();
// Set drive info
i686_fdc_drive_data(13, 1, 0x0F, true);
}

Veure arxiu

@ -0,0 +1,70 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#define i686_FDC_DOR 0x3F2
#define i686_FDC_MSR 0x3F4
#define i686_FDC_FIFO 0x3F5
#define i686_FDC_CTRL 0x3F7
#define i686_FDC_DOR_DRIVE0 0x00
#define i686_FDC_DOR_DRIVE1 0x01
#define i686_FDC_DOR_DRIVE2 0x02
#define i686_FDC_DOR_DRIVE3 0x03
#define i686_FDC_DOR_RESET 0x04
#define i686_FDC_DOR_DMA 0x08
#define i686_FDC_DOR_DRIVE0_MOTOR 0x10
#define i686_FDC_DOR_DRIVE1_MOTOR 0x20
#define i686_FDC_DOR_DRIVE2_MOTOR 0x40
#define i686_FDC_DOR_DRIVE3_MOTOR 0x80
#define i686_FDC_MSR_DRIVE0_POS_MODE 0x01
#define i686_FDC_MSR_DRIVE1_POS_MODE 0x02
#define i686_FDC_MSR_DRIVE2_POS_MODE 0x04
#define i686_FDC_MSR_DRIVE3_POS_MODE 0x08
#define i686_FDC_MSR_BUSY 0x10
#define i686_FDC_MSR_DMA 0x20
#define i686_FDC_MSR_DATAIO 0x40
#define i686_FDC_MSR_DATAREG 0x80
#define i686_FDC_CMD_READ_TRACK 0x02
#define i686_FDC_CMD_SPECIFY 0x03
#define i686_FDC_CMD_CHECK_STAT 0x04
#define i686_FDC_CMD_WRITE_SECT 0x05
#define i686_FDC_CMD_READ_SECT 0x06
#define i686_FDC_CMD_CALIBRATE 0x07
#define i686_FDC_CMD_CHECK_INT 0x08
#define i686_FDC_CMD_WRITE_DEL_S 0x09
#define i686_FDC_CMD_READ_ID_S 0x0A
#define i686_FDC_CMD_READ_DEL_S 0x0C
#define i686_FDC_CMD_FORMAT_TRACK 0x0D
#define i686_FDC_CMD_SEEK 0x0F
#define i686_FDC_CMD_EXT_SKIP 0x20
#define i686_FDC_CMD_EXT_DENSITY 0x40
#define i686_FDC_CMD_EXT_MULTITRACK 0x80
#define i686_FDC_GAP3_STD 42
#define i686_FDC_GAP3_5_14 32
#define i686_FDC_GAP3_3_5 27
#define i686_FDC_SECTOR_DTL_128 0
#define i686_FDC_SECTOR_DTL_256 1
#define i686_FDC_SECTOR_DTL_512 2
#define i686_FDC_SECTOR_DTL_1024 4
#define i686_FDC_SECTORS_PER_TRACK 18
void i686_fdc_initialize_dma();
void i686_fdc_dma_read();
void i686_fdc_dma_write();
uint8_t i686_fdc_msr_read();
void i686_fdc_send_cmd(uint8_t cmd);
uint8_t i686_fdc_read_data();
void i686_fdc_write_ccr(uint8_t v);
void i686_fdc_drive_data(uint32_t stepr, uint32_t loadt, uint32_t unloadt, bool dma);
void i686_fdc_check_int(uint32_t *st0, uint32_t *cyl);
int i686_fdc_motor(bool b);
void i686_fdc_read_sector_imp(uint8_t head, uint8_t track, uint8_t sector);
uint8_t *i686_fdc_read_sector(int lba);
int i686_fdc_calibrate(uint32_t drive);
int i686_fdc_seek(uint32_t cyl, uint32_t head);
void i686_fdc_disable();
void i686_fdc_enable();
void i686_fdc_reset();
void i686_fdc_set_working_drive(int drive);
void i686_fdc_initialize();

Veure arxiu

@ -0,0 +1,19 @@
#include "pit.h"
#include "isr.h"
#include "../../lib/stdio.h"
#include "../../lib/debug.h"
static volatile uint32_t g_tick_count = 0;
static void i686_pit_handler(registers *regs){
g_tick_count++;
}
void i686_pit_initialize(){
debug_printf("[PIT] Initializing\n");
i686_isr_register_handler(IRQ(0), i686_pit_handler);
}
uint32_t i686_pit_get_tick_count(){
return g_tick_count;
}

Veure arxiu

@ -0,0 +1,6 @@
#pragma once
#include <stdint.h>
void i686_pit_initialize();
uint32_t i686_pit_get_tick_count();

Veure arxiu

@ -11,54 +11,66 @@
static bool g_caps_lock = false;
static bool g_shift = false;
static char g_key_buffer[256];
static struct {
char buffer[KEY_BUFFER_LENGTH];
int base;
int top;
} g_key_buffer;
void keyboard_buffer_add(char c){
g_key_buffer.buffer[g_key_buffer.top] = c;
g_key_buffer.top++;
g_key_buffer.top %= KEY_BUFFER_LENGTH;
}
char keyboard_buffer_get(){
char c = g_key_buffer.buffer[g_key_buffer.base];
g_key_buffer.base++;
g_key_buffer.base %= KEY_BUFFER_LENGTH;
return c;
}
int keyboard_buffer_length(){
int base = g_key_buffer.base;
int top = g_key_buffer.top;
if(top < base) top += KEY_BUFFER_LENGTH;
return top - base;
}
static const char g_sc_ascii[128] = {
'?', '?', '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '-', '=', '?', '?',
'7', '8', '9', '0', '-', '=', '\b', '?',
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '[', ']', '?', '?', 'a', 's',
'o', 'p', '[', ']', '\n', '?', 'a', 's',
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
'\'', '`', '?', '\\', 'z', 'x', 'c', 'v',
'b', 'n', 'm', ',', '.', '/', '?', '?',
'?', ' '
};
static void user_input(char *str){
shell_run(str);
}
static void keyboard_callback(){
uint8_t scancode = i686_inb(0x60);
if(scancode == 0x2A) g_shift = true; // Shift pressed
else if(scancode == 0x2A + 128) g_shift = false; // Shift released
else if(scancode == 0x3A) g_caps_lock = !g_caps_lock; // Caps lock pressed
else if(scancode < 128){ // If it's a "pressed" event, not "released"
if(scancode == 0x0E){ // Backspace
putc('\b');
g_key_buffer[strlen(g_key_buffer)-1] = '\0';
}
else if(scancode == 0x1C){ // Enter
putc('\n');
user_input(g_key_buffer);
g_key_buffer[0] = '\0';
}
else{
char c = g_sc_ascii[scancode];
if(c >= 'a' && c <= 'z' && g_shift != g_caps_lock) c += 'A' - 'a';
putc(c);
char c = g_sc_ascii[scancode];
if(c >= 'a' && c <= 'z' && g_shift != g_caps_lock) c += 'A' - 'a';
// strlen() works here because char[] is the same as
// char*, and both are null-terminated, so they are
// exactly the same
int len = strlen(g_key_buffer);
g_key_buffer[len] = c;
g_key_buffer[len+1] = '\0';
}
// strlen() works here because char[] is the same as
// char*, and both are null-terminated, so they are
// exactly the same
keyboard_buffer_add(c);
}
}
void keyboard_initialize(){
debug_printf("[KEYBOARD] Initializing keyboard\n");
debug_printf("[KEYBOARD] Initializing\n");
// Setup IRQ
i686_isr_register_handler(IRQ(1), keyboard_callback);
// Initialize key buffer
g_key_buffer.base = 0;
g_key_buffer.top = 0;
}

Veure arxiu

@ -1,3 +1,7 @@
#pragma once
#define KEY_BUFFER_LENGTH 16
char keyboard_buffer_get();
int keyboard_buffer_length();
void keyboard_initialize();

Veure arxiu

@ -1,13 +0,0 @@
#include "timer.h"
#include "../arch/i686/isr.h"
#include "../lib/stdio.h"
#include "../lib/debug.h"
static void timer_handler(registers *regs){
//printf("TICK\n");
}
void timer_initialize(){
debug_printf("[TIMER] Initializing PIT\n");
i686_isr_register_handler(IRQ(0), timer_handler);
}

Veure arxiu

@ -1,3 +0,0 @@
#pragma once
void timer_initialize();

Veure arxiu

@ -2,9 +2,15 @@
#include "../arch/i686/gdt.h"
#include "../arch/i686/idt.h"
#include "../arch/i686/isr.h"
#include "../arch/i686/pit.h"
void hal_initialize(){
i686_gdt_initialize();
i686_idt_initialize();
i686_isr_initialize();
i686_pit_initialize();
}
uint32_t hal_get_tick_count(){
return i686_pit_get_tick_count();
}

Veure arxiu

@ -1,3 +1,6 @@
#pragma once
#include <stdint.h>
void hal_initialize();
uint32_t hal_get_tick_count();

Veure arxiu

@ -3,6 +3,7 @@
#include "stdio.h"
#include "../drivers/vga.h"
#include "../drivers/uart.h"
#include "../drivers/keyboard.h"
typedef void (*print_callable_fn_t)(char);
@ -128,6 +129,34 @@ void serial_printf(char *fmt, ...){
_vprintf(serial_putc, fmt, args);
}
void gets(char *str, int max){
int count = 0;
str[0] = '\0';
for(;;){
if(keyboard_buffer_length()){
char c = keyboard_buffer_get();
if(c == '\n'){
putc(c);
return;
}
else if(c == '\b'){
if(count){
putc(c);
str[count--] = '\0';
}
}
else{
if(count < max){
putc(c);
str[count] = c;
str[++count] = '\0';
}
}
}
}
}
void clear_screen(){
vga_fill_screen(' ', VGA_COLOR_BG_BLACK | VGA_COLOR_FG_WHITE);
vga_set_cursor(0);

Veure arxiu

@ -9,4 +9,5 @@ void vprintf(char *fmt, va_list args);
void printf(char *fmt, ...);
void serial_vprintf(char *fmt, va_list args);
void serial_printf(char *fmt, ...);
void gets(char *str, int max);
void clear_screen();

Veure arxiu

@ -17,3 +17,14 @@ void strcpy(char *dest, char *src){
}
*dest = '\0';
}
int atoi(char *str){
int res = 0;
for(int i = 0; i < strlen(str); i++){
res *= 10;
res += str[i] - '0';
}
return res;
}

Veure arxiu

@ -3,3 +3,4 @@
int strlen(char *s);
int strcmp(char *s1, char *s2);
void strcpy(char *dest, char *src);
int atoi(char *str);

8
src/kernel/lib/time.c Normal file
Veure arxiu

@ -0,0 +1,8 @@
#include <stdint.h>
#include "time.h"
#include "../hal/hal.h"
void sleep(int ms){
uint32_t ticks = ms + hal_get_tick_count();
while(ticks > hal_get_tick_count());
}

3
src/kernel/lib/time.h Normal file
Veure arxiu

@ -0,0 +1,3 @@
#pragma once
void sleep(int ms);

Veure arxiu

@ -1,7 +1,6 @@
#include <stdint.h>
#include "lib/stdio.h"
#include "hal/hal.h"
#include "drivers/timer.h"
#include "drivers/keyboard.h"
#include "drivers/uart.h"
#include "fs/vfs.h"
@ -9,6 +8,7 @@
#include "arch/i686/mm/vmm.h"
#include "multiboot.h"
#include "lib/debug.h"
#include "arch/i686/fdc.h"
extern uint8_t end; // Kernel end
@ -23,7 +23,6 @@ void __attribute__((cdecl)) kmain(multiboot_info_t *multiboot_info){
uart_initialize(COM1, 2);
// Initialize drivers
timer_initialize();
keyboard_initialize();
// Initialize physical memory manager
@ -31,6 +30,8 @@ void __attribute__((cdecl)) kmain(multiboot_info_t *multiboot_info){
i686_pmm_initialize(mem_size, (int)(&end));
// Memory map
debug_printf("= Memory map =\n");
for(int i = 0; i < multiboot_info->mmap_length; i += sizeof(multiboot_memory_map_t)){
multiboot_memory_map_t *memory_map = (multiboot_memory_map_t *)(multiboot_info->mmap_addr + i);
debug_printf("Start Addr: 0x%x%x | Length: 0x%x%x | Size: 0x%x | Type: %i\n", memory_map->addr_h, memory_map->addr_l, memory_map->len_h, memory_map->len_l, memory_map->size, memory_map->type);
@ -45,8 +46,17 @@ void __attribute__((cdecl)) kmain(multiboot_info_t *multiboot_info){
// Initialize virtual memory
i686_vmm_initialize();
// Floppy disk controller
i686_fdc_set_working_drive(0);
i686_fdc_initialize();
// Initialize FS
vfs_initialize();
// Run shell
shell();
// Halt at end
debug_printf("Kernel execution ended\n");
for(;;);
}

Veure arxiu

@ -1,7 +1,10 @@
#include <stdint.h>
#include "shell.h"
#include "lib/stdio.h"
#include "lib/string.h"
#include "lib/memory.h"
#include "lib/time.h"
#include "arch/i686/fdc.h"
int shell_run(char *cmd){
int ret = 0;
@ -19,6 +22,35 @@ int shell_run(char *cmd){
else if(!strcmp(cmd, "serial")){
serial_printf("Hello world!\n");
}
else if(!strcmp(cmd, "sleep")){
printf("Sleeping...\n");
sleep(25);
printf("Done!\n");
}
else if(!strcmp(cmd, "readsect")){
puts("Sector number: ");
char secn_str[4];
gets(secn_str, 3);
int secn = atoi(secn_str);
uint8_t *sector = i686_fdc_read_sector(secn);
if(sector){
int i = 0;
for(int c = 0; c < 4; c++){
for(int j = 0; j < 128; j++){
printf("0x%x ", sector[i+j]);
}
i += 128;
printf("\nPress enter to continue\n");
gets(NULL, NULL);
}
}
else{
printf("ERROR reading sector %i from disk", secn);
}
printf("Done!\n");
}
else{
printf("SHELL: Unknown command \"%s\"\n", cmd);
ret = 127;
@ -27,3 +59,14 @@ int shell_run(char *cmd){
return ret;
}
void shell(){
puts("\x1b[33mWelcome to quark!\x1b[0m\n\n");
for(;;){
static char cmd_buffer[256];
puts("$>");
gets(cmd_buffer, 255);
shell_run(cmd_buffer);
}
}

Veure arxiu

@ -1,3 +1,3 @@
#pragma once
int shell_run();
void shell();