Compare commits
2 Commits
d9a78dbcd5
...
dacdc0ae8d
Author | SHA1 | Date | |
---|---|---|---|
dacdc0ae8d | |||
716fb09a77 |
Binary file not shown.
Binary file not shown.
6
Makefile
6
Makefile
|
@ -1,7 +1,7 @@
|
|||
CC = i686-elf-gcc
|
||||
INCLUDE = -I./
|
||||
CFLAGS = -Wall -Wextra -Werror -ffreestanding $(INCLUDE)
|
||||
LDFLAGS = -T kernel.ld -ffreestanding -O3 -nostdlib -lgcc -mpreferred-stack-boundary=4
|
||||
LDFLAGS = -T kernel.ld -ffreestanding -O3 -nostdlib -lgcc
|
||||
|
||||
BUILDDIR = build
|
||||
|
||||
|
@ -21,7 +21,7 @@ KERNELIMG := $(BUILDDIR)/kernel.bin
|
|||
QEMUFLAGS = -d int -s \
|
||||
-kernel $(KERNELIMG) \
|
||||
-machine q35 \
|
||||
-device piix3-ide,id=ide -drive id=disk,file=image.img,format=raw,if=none -device ide-hd,drive=disk,bus=ide.0
|
||||
-device piix3-ide,id=ide -drive id=disk,file=image.img,format=raw,if=none -device ide-hd,drive=disk,bus=ide.0 \
|
||||
|
||||
.PHONY: all kernel qemu clean docs
|
||||
|
||||
|
@ -44,6 +44,8 @@ qemu: kernel
|
|||
iso: kernel
|
||||
cp $(BUILDDIR)/kernel.bin iso/boot/kernel.bin
|
||||
grub-mkrescue -o $(BUILDDIR)/andewOS.iso iso/
|
||||
bochs: iso
|
||||
bochs -q
|
||||
|
||||
clean:
|
||||
rm -rf build/*.o build/*.bin iso/boot/*.bin
|
||||
|
|
20
acpi.c
20
acpi.c
|
@ -1,6 +1,7 @@
|
|||
#include <acpi.h>
|
||||
#include <strcmp.h>
|
||||
|
||||
struct rsdp *acpi_search_for_rsdp() {
|
||||
struct rsdp *acpi_locate_rsdp() {
|
||||
char *ptr = (char *)BIOS_START;
|
||||
char *str = RSDP_SIG;
|
||||
|
||||
|
@ -37,3 +38,20 @@ int acpi_validate_sdt_checksum(struct ACPISDTHeader *s) {
|
|||
|
||||
return sum == 0;
|
||||
}
|
||||
|
||||
void *acpi_locate_sdt(struct rsdt *rsdt, const char sig[4]) {
|
||||
int entries = (rsdt->h.Length - sizeof(rsdt->h)) / 4;
|
||||
|
||||
for (int i = 0; i < entries; i++)
|
||||
{
|
||||
struct ACPISDTHeader *h = (struct ACPISDTHeader *) rsdt->other_sdt[i];
|
||||
if (!strncmp(h->Signature, sig, 4)) {
|
||||
if (acpi_validate_sdt_checksum(h))
|
||||
return (void *) h;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
79
acpi.h
79
acpi.h
|
@ -51,9 +51,86 @@ struct mcfg {
|
|||
struct mcfg_entry entries[];
|
||||
};
|
||||
|
||||
struct rsdp *acpi_search_for_rsdp();
|
||||
struct GenericAddressStructure
|
||||
{
|
||||
uint8_t AddressSpace;
|
||||
uint8_t BitWidth;
|
||||
uint8_t BitOffset;
|
||||
uint8_t AccessSize;
|
||||
uint64_t Address;
|
||||
};
|
||||
|
||||
struct fadt {
|
||||
struct ACPISDTHeader h;
|
||||
uint32_t FirmwareCtrl;
|
||||
uint32_t Dsdt;
|
||||
|
||||
// field used in ACPI 1.0; no longer in use, for compatibility only
|
||||
uint8_t Reserved;
|
||||
|
||||
uint8_t PreferredPowerManagementProfile;
|
||||
uint16_t SCI_Interrupt;
|
||||
uint32_t SMI_CommandPort;
|
||||
uint8_t AcpiEnable;
|
||||
uint8_t AcpiDisable;
|
||||
uint8_t S4BIOS_REQ;
|
||||
uint8_t PSTATE_Control;
|
||||
uint32_t PM1aEventBlock;
|
||||
uint32_t PM1bEventBlock;
|
||||
uint32_t PM1aControlBlock;
|
||||
uint32_t PM1bControlBlock;
|
||||
uint32_t PM2ControlBlock;
|
||||
uint32_t PMTimerBlock;
|
||||
uint32_t GPE0Block;
|
||||
uint32_t GPE1Block;
|
||||
uint8_t PM1EventLength;
|
||||
uint8_t PM1ControlLength;
|
||||
uint8_t PM2ControlLength;
|
||||
uint8_t PMTimerLength;
|
||||
uint8_t GPE0Length;
|
||||
uint8_t GPE1Length;
|
||||
uint8_t GPE1Base;
|
||||
uint8_t CStateControl;
|
||||
uint16_t WorstC2Latency;
|
||||
uint16_t WorstC3Latency;
|
||||
uint16_t FlushSize;
|
||||
uint16_t FlushStride;
|
||||
uint8_t DutyOffset;
|
||||
uint8_t DutyWidth;
|
||||
uint8_t DayAlarm;
|
||||
uint8_t MonthAlarm;
|
||||
uint8_t Century;
|
||||
|
||||
// reserved in ACPI 1.0; used since ACPI 2.0+
|
||||
uint16_t BootArchitectureFlags;
|
||||
|
||||
uint8_t Reserved2;
|
||||
uint32_t Flags;
|
||||
|
||||
// 12 byte structure; see below for details
|
||||
struct GenericAddressStructure ResetReg;
|
||||
|
||||
uint8_t ResetValue;
|
||||
uint8_t Reserved3[3];
|
||||
|
||||
// 64bit pointers - Available on ACPI 2.0+
|
||||
uint64_t X_FirmwareControl;
|
||||
uint64_t X_Dsdt;
|
||||
|
||||
struct GenericAddressStructure X_PM1aEventBlock;
|
||||
struct GenericAddressStructure X_PM1bEventBlock;
|
||||
struct GenericAddressStructure X_PM1aControlBlock;
|
||||
struct GenericAddressStructure X_PM1bControlBlock;
|
||||
struct GenericAddressStructure X_PM2ControlBlock;
|
||||
struct GenericAddressStructure X_PMTimerBlock;
|
||||
struct GenericAddressStructure X_GPE0Block;
|
||||
struct GenericAddressStructure X_GPE1Block;
|
||||
};
|
||||
|
||||
struct rsdp *acpi_locate_rsdp();
|
||||
int acpi_validate_rsdp_checksum(struct rsdp *s);
|
||||
int acpi_validate_sdt_checksum(struct ACPISDTHeader *s);
|
||||
struct ACPISDTHeader *acpi_find_table(struct rsdt *root, const char *signature);
|
||||
void *acpi_locate_sdt(struct rsdt *rsdt, const char sig[4]);
|
||||
|
||||
#endif
|
||||
|
|
3
bochsrc
Normal file
3
bochsrc
Normal file
|
@ -0,0 +1,3 @@
|
|||
ata0-slave: type=cdrom, path=./build/andewOS.iso, status=inserted
|
||||
magic_break: enabled=1
|
||||
boot: cdrom
|
9
gdt.h
Normal file
9
gdt.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#ifndef GDT_H_
|
||||
#define GDT_H_
|
||||
|
||||
#define GDT_NULL 0
|
||||
#define GDT_KERNEL_CODE 1
|
||||
#define GDT_KERNEL_DATA 2
|
||||
#define GDT_SEGMENT_SELECTOR(i, p) ((i << 3) | p)
|
||||
|
||||
#endif
|
15
ide.h
Normal file
15
ide.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#ifndef IDE_H_
|
||||
#define IDE_H_
|
||||
|
||||
enum IDE_MODE {
|
||||
ISA_ONLY = 0x00,
|
||||
PCI_ONLY = 0x05,
|
||||
ISA_CAN_SWITCH = 0x0a,
|
||||
PCI_CAN_SWITCH = 0x0f,
|
||||
ISA_ONLY_BUS_MASTERING = 0x80,
|
||||
PCI_ONLY_BUS_MASTERING = 0x85,
|
||||
ISA_CAN_SWITCH_BUS_MASTERING = 0x8a,
|
||||
PCI_CAN_SWITCH_BUS_MASTERING = 0x8f,
|
||||
};
|
||||
|
||||
#endif
|
9
idt.c
Normal file
9
idt.c
Normal file
|
@ -0,0 +1,9 @@
|
|||
#include <idt.h>
|
||||
|
||||
void encode_idt_entry(struct idt_entry idt[], int i, uint32_t offset, uint16_t segment_selector, uint8_t attributes) {
|
||||
idt[i].offset_lo = (uint16_t)(offset & 0xFFFF);
|
||||
idt[i].seg_select = segment_selector;
|
||||
idt[i].reserved = 0;
|
||||
idt[i].attributes = attributes;
|
||||
idt[i].offset_hi = (uint16_t)(offset >> 16);
|
||||
}
|
42
idt.h
Normal file
42
idt.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
#ifndef ANDEWOS_IDT_H_
|
||||
#define ANDEWOS_IDT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define GDT_SEGMENT_SELECTOR(i, p) ((i << 3) | p)
|
||||
#define LDT_SEGMENT_SELECTOR(i, p) ((i << 3) | 0x04 | p)
|
||||
|
||||
#define IDT_ENTRY_COUNT 256
|
||||
|
||||
struct idt_entry {
|
||||
uint16_t offset_lo;
|
||||
uint16_t seg_select;
|
||||
uint8_t reserved;
|
||||
uint8_t attributes;
|
||||
uint16_t offset_hi;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct idtr {
|
||||
uint16_t size;
|
||||
uint32_t base;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
enum IDT_ATTRIBUTES {
|
||||
TASK_GATE = 0x05,
|
||||
INT_GATE_16 = 0x06,
|
||||
TRAP_GATE_16 = 0x07,
|
||||
INT_GATE_32 = 0x0E,
|
||||
TRAP_GATE_32 = 0x0F,
|
||||
|
||||
INT_RING0 = 0x00,
|
||||
INT_RING1 = 0x20,
|
||||
INT_RING2 = 0x40,
|
||||
INT_RING3 = 0x60,
|
||||
|
||||
INT_PRESENT = 0x80,
|
||||
};
|
||||
|
||||
void encode_idt_entry(struct idt_entry idt[], int i, uint32_t offset, uint16_t segment_selector, uint8_t attributes);
|
||||
void flush_idt(uint32_t idt_pointer);
|
||||
|
||||
#endif
|
5
iso/boot/grub/grub.cfg
Normal file
5
iso/boot/grub/grub.cfg
Normal file
|
@ -0,0 +1,5 @@
|
|||
default=0
|
||||
timeout=0
|
||||
menuentry "andewOS" {
|
||||
multiboot /boot/kernel.bin
|
||||
}
|
104
kmain.c
104
kmain.c
|
@ -1,8 +1,13 @@
|
|||
#include <multiboot.h>
|
||||
#include <acpi.h>
|
||||
#include <vga.h>
|
||||
#include <pci.h>
|
||||
#include <printf.h>
|
||||
#include <strcmp.h>
|
||||
#include <gdt.h>
|
||||
#include <idt.h>
|
||||
#include <pic8259.h>
|
||||
#include <ps2.h>
|
||||
|
||||
struct kernel_context {
|
||||
struct rsdp *rsdp;
|
||||
|
@ -13,58 +18,30 @@ struct kernel_context {
|
|||
|
||||
struct kernel_context ctx = {0};
|
||||
|
||||
struct idt_entry g_idt[256] = {0};
|
||||
struct idtr g_idtr = {0};
|
||||
|
||||
void *acpi_find_sdt(void *RootSDT, const char sig[4]) {
|
||||
struct rsdt *rsdt = (struct rsdt *) RootSDT;
|
||||
int entries = (rsdt->h.Length - sizeof(rsdt->h)) / 4;
|
||||
|
||||
for (int i = 0; i < entries; i++)
|
||||
{
|
||||
struct ACPISDTHeader *h = (struct ACPISDTHeader *) rsdt->other_sdt[i];
|
||||
if (!strncmp(h->Signature, sig, 4))
|
||||
return (void *) h;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
extern void exception_handler();
|
||||
extern void ps2_isr();
|
||||
|
||||
uint16_t readw(uint32_t addr, uint32_t offset, uint8_t bus, uint8_t slot, uint8_t func) {
|
||||
uint32_t lbus = (uint32_t)bus;
|
||||
uint32_t lslot = (uint32_t)slot;
|
||||
uint32_t lfunc = (uint32_t)func;
|
||||
|
||||
uint32_t address = addr + offset + ((lbus) << 20 | lslot << 15 | lfunc << 12);
|
||||
|
||||
return *(uint16_t *)address;
|
||||
}
|
||||
|
||||
uint8_t readb(uint32_t addr, uint32_t offset, uint8_t bus, uint8_t slot, uint8_t func) {
|
||||
uint32_t lbus = (uint32_t)bus;
|
||||
uint32_t lslot = (uint32_t)slot;
|
||||
uint32_t lfunc = (uint32_t)func;
|
||||
|
||||
uint32_t address = addr + offset + ((lbus) << 20 | lslot << 15 | lfunc << 12);
|
||||
|
||||
return *(uint8_t *)address;
|
||||
}
|
||||
|
||||
uint32_t readd(uint32_t addr, uint32_t offset, uint8_t bus, uint8_t slot, uint8_t func) {
|
||||
uint32_t lbus = (uint32_t)bus;
|
||||
uint32_t lslot = (uint32_t)slot;
|
||||
uint32_t lfunc = (uint32_t)func;
|
||||
|
||||
uint32_t address = addr + offset + ((lbus) << 20 | lslot << 15 | lfunc << 12);
|
||||
|
||||
return *(uint32_t *)address;
|
||||
}
|
||||
#define MAGIC_BREAKPOINT __asm__ volatile("xchgw %bx, %bx");
|
||||
|
||||
void kmain(struct multiboot_info *info) {
|
||||
clear_screen();
|
||||
|
||||
// Check if the bootloader gave us the upper and lower memory
|
||||
// Check if the bootloader gave us the upper and lower memory
|
||||
if (!(info->flags & 0x1)) goto halt;
|
||||
|
||||
struct rsdp *found_rsdp = acpi_search_for_rsdp();
|
||||
// Create and load an IDT
|
||||
for (int i = 0; i < 32; i++) {
|
||||
encode_idt_entry(g_idt, i, (uint32_t)&exception_handler, GDT_SEGMENT_SELECTOR(GDT_KERNEL_CODE, 0x00), TRAP_GATE_32 | INT_RING0 | INT_PRESENT);
|
||||
}
|
||||
|
||||
g_idtr.size = (sizeof(struct idt_entry) * 256) - 1;
|
||||
g_idtr.base = (uint32_t) &g_idt;
|
||||
|
||||
asm volatile ("lidt %0" :: "m"(g_idtr));
|
||||
|
||||
struct rsdp *found_rsdp = acpi_locate_rsdp();
|
||||
if (!found_rsdp) {
|
||||
printf("Failed to find RSDP signature\n");
|
||||
goto halt;
|
||||
|
@ -73,7 +50,6 @@ void kmain(struct multiboot_info *info) {
|
|||
printf("RSDP detected at 0x%X\n", found_rsdp);
|
||||
|
||||
if (found_rsdp->Revision == ACPI_VER_1) {
|
||||
// ACPI v1.0 is being used
|
||||
if (!acpi_validate_rsdp_checksum(found_rsdp)) {
|
||||
printf("RSDP has an invalid checksum\n");
|
||||
goto halt;
|
||||
|
@ -100,26 +76,30 @@ void kmain(struct multiboot_info *info) {
|
|||
|
||||
printf("Using ACPI v1.0\n");
|
||||
|
||||
struct mcfg *mcfg = acpi_find_sdt(ctx.rsdt, "MCFG");
|
||||
if (!mcfg) {
|
||||
printf("Failed to find MCFG\n");
|
||||
struct fadt *fadt = acpi_locate_sdt(ctx.rsdt, "FACP");
|
||||
if (!fadt) {
|
||||
printf("Failed to find FADT\n");
|
||||
goto halt;
|
||||
}
|
||||
printf("Found FADT at 0x%x\n", fadt);
|
||||
if (fadt->Flags & 1)
|
||||
printf("Legacy devices are supported.\n");
|
||||
pic_remap(PIC_1_START, PIC_2_START);
|
||||
// ACPI version 1.0 is so old that we assume that our PC supports the 8042 ps/2 controller
|
||||
initialize_8042ps2();
|
||||
encode_idt_entry(g_idt, 0x21, (uint32_t)&ps2_isr, GDT_SEGMENT_SELECTOR(GDT_KERNEL_CODE, 0x00), INT_GATE_32 | INT_RING0 | INT_PRESENT);
|
||||
asm volatile ("sti" ::);
|
||||
|
||||
printf("Detected MCFG at 0x%X\n", mcfg);
|
||||
int entries = (mcfg->h.Length - 44) / 16;
|
||||
for (int i = 0; i < entries; i++) {
|
||||
uint32_t addr = (uint32_t)mcfg->entries[i].base_addr;
|
||||
for (int j = 0; j < 256; j++) {
|
||||
for (int k = 0; k < 8; k++) {
|
||||
uint32_t word = readd(addr, 8, i, j, k);
|
||||
if (word != 0xFFFFFFFF && word) {
|
||||
printf("%08X\n", word >> 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
struct mcfg *mcfg = acpi_locate_sdt(ctx.rsdt, "MCFG");
|
||||
if (!mcfg) {
|
||||
printf("failed to find mcfg\n");
|
||||
} else {
|
||||
|
||||
printf("Found MCFG at 0x%x\n", mcfg);
|
||||
|
||||
struct pci_config_space *ide = pcie_find_device(mcfg, MASS_STORAGE_CONTROLLER, IDE_INTERFACE);
|
||||
if (ide) printf("IDE controller detected. Program Interface: %X\n", ide->program_interface);
|
||||
}
|
||||
|
||||
halt:
|
||||
for (;;) {}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ _start:
|
|||
jmp 0x8:.use_code_seg ; Offset to kernel code descriptor
|
||||
.use_code_seg:
|
||||
mov esp, stack_bottom
|
||||
mov ebp, esp
|
||||
push ebx
|
||||
extern kmain
|
||||
call kmain
|
||||
|
@ -37,6 +38,19 @@ _start:
|
|||
halt:
|
||||
hlt
|
||||
jmp halt
|
||||
global exception_handler
|
||||
exception_handler:
|
||||
add esp, 4
|
||||
mov byte [0xb8000], 'X'
|
||||
iret
|
||||
global ps2_isr
|
||||
ps2_isr:
|
||||
extern ps2_handler
|
||||
pushad
|
||||
cld
|
||||
call ps2_handler
|
||||
popad
|
||||
iret
|
||||
|
||||
; GDT for a flat memory layout
|
||||
; We get access to all memory and can utilize paging
|
||||
|
@ -63,9 +77,10 @@ gdtp:
|
|||
dw gdt_end - gdt_start - 1
|
||||
dd gdt_start
|
||||
|
||||
|
||||
; Reserve 16KiB of kernel stack space
|
||||
section .bss
|
||||
align 4
|
||||
align 16
|
||||
stack_top:
|
||||
resb 16384
|
||||
stack_bottom:
|
||||
|
|
18
pci.c
Normal file
18
pci.c
Normal file
|
@ -0,0 +1,18 @@
|
|||
#include <pci.h>
|
||||
|
||||
struct pci_config_space *pcie_find_device(struct mcfg *mcfg, uint8_t class, uint8_t subclass) {
|
||||
int entries = (mcfg->h.Length - 44) / 16;
|
||||
for (int i = 0; i < entries; i++) {
|
||||
uint32_t addr = (uint32_t)mcfg->entries[i].base_addr;
|
||||
for (int j = 0; j < 256; j++) {
|
||||
for (int k = 0; k < 8; k++) {
|
||||
struct pci_config_space *cfg = (struct pci_config_space *)PCI_ADDR(addr, i, j, k);
|
||||
if (cfg->class == class && cfg->subclass == subclass) {
|
||||
return cfg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
56
pci.h
Normal file
56
pci.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
#ifndef PCI_H_
|
||||
#define PCI_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <acpi.h>
|
||||
|
||||
#define PCI_ADDR(addr, bus, device, func) (addr + ((bus) << 20 | device << 15 | func << 12))
|
||||
|
||||
enum PCI_CLASS {
|
||||
UNCLASSIFIED,
|
||||
MASS_STORAGE_CONTROLLER,
|
||||
NETWORK_CONTROLLER,
|
||||
DISPLAY_CONTROLLER,
|
||||
MULTIMEDIA_CONTROLLER,
|
||||
MEMORY_CONTROLLER,
|
||||
BRIDGE,
|
||||
COMMUNICATION_CONTROLLER,
|
||||
GENERIC_SYSTEM_PERIPHERAL,
|
||||
INPUT_DEVICE_CONTROLLER,
|
||||
DOCKING_STATION,
|
||||
PROCESSOR,
|
||||
SERIAL_BUS_CONTROLLER,
|
||||
WIRELESS_CONTROLLER,
|
||||
INTELLIGENT_CONTROLLER,
|
||||
SATELLITE_COMMUNICATIONS_CONTROLLER,
|
||||
ENCRYPTION_CONTROLLER,
|
||||
SIGNAL_PROCESSING_CONTROLLER,
|
||||
PROCESSING_ACCELERATORS,
|
||||
};
|
||||
|
||||
enum PCI_STORAGE_SUBCLASS {
|
||||
SCSI_STORAGE_CONTROLLER,
|
||||
IDE_INTERFACE,
|
||||
FLOPPY_DISK_CONTROLLER,
|
||||
IPI_BUS_CONTROLLER,
|
||||
RAID_BUS_CONTROLLER,
|
||||
ATA_CONTROLLER,
|
||||
SATA_CONTROLLER,
|
||||
};
|
||||
|
||||
struct pci_config_space {
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
uint16_t command;
|
||||
uint16_t status;
|
||||
uint8_t revision_id;
|
||||
uint8_t program_interface;
|
||||
uint8_t subclass;
|
||||
uint8_t class;
|
||||
};
|
||||
|
||||
// TODO: Bruteforce all devices once then store all valid devices in memory rather than bruteforcing every time to search
|
||||
// TODO: Returning multiple devices
|
||||
struct pci_config_space *pcie_find_device(struct mcfg *mcfg, uint8_t class, uint8_t subclass);
|
||||
|
||||
#endif
|
30
pic8259.c
Normal file
30
pic8259.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include <pic8259.h>
|
||||
|
||||
void pic_send_eoi(uint8_t irq) {
|
||||
if (irq > 7)
|
||||
outportb(PIC_2_COMMAND, PIC_EOI);
|
||||
else
|
||||
outportb(PIC_1_COMMAND, PIC_EOI);
|
||||
}
|
||||
|
||||
void pic_remap(int pic1_start, int pic2_start) {
|
||||
// Mask all IRQ lines
|
||||
outportb(PIC_1_DATA, 0xff);
|
||||
outportb(PIC_2_DATA, 0xff);
|
||||
|
||||
// Initialize master PIC
|
||||
outportb(PIC_1_COMMAND, ICW4_NEEDED | INIT_BIT);
|
||||
outportb(PIC_1_DATA, pic1_start);
|
||||
outportb(PIC_1_DATA, PIC_1_ICW3);
|
||||
outportb(PIC_1_DATA, 0x01);
|
||||
|
||||
// Initialize slave PIC
|
||||
outportb(PIC_2_COMMAND, ICW4_NEEDED | INIT_BIT);
|
||||
outportb(PIC_2_DATA, pic2_start);
|
||||
outportb(PIC_2_DATA, PIC_2_ICW3);
|
||||
outportb(PIC_2_DATA, 0x01);
|
||||
|
||||
// Unmask
|
||||
outportb(PIC_1_DATA, 0b11111101);
|
||||
outportb(PIC_2_DATA, 0b11111101);
|
||||
}
|
30
pic8259.h
Normal file
30
pic8259.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef ANDEWOS_PIC8259_H
|
||||
#define ANDEWOS_PIC8259_H
|
||||
|
||||
#define PIC_1_COMMAND 0x20
|
||||
#define PIC_2_COMMAND 0xA0
|
||||
#define PIC_1_DATA 0x21
|
||||
#define PIC_2_DATA 0xA1
|
||||
|
||||
#define PIC_EOI 0x20
|
||||
|
||||
#define PIC_1_START 32
|
||||
#define PIC_2_START 40
|
||||
|
||||
#define PIC_1_ICW3 0x04
|
||||
#define PIC_2_ICW3 0x02
|
||||
|
||||
#include <io.h>
|
||||
|
||||
enum ICW1 {
|
||||
ICW4_NEEDED = 0x01,
|
||||
SINGLE_MODE = 0x02,
|
||||
INTERVAL_4 = 0x04,
|
||||
LVL_TRIGGER = 0x08,
|
||||
INIT_BIT = 0x10,
|
||||
};
|
||||
|
||||
void pic_send_eoi(uint8_t irq);
|
||||
void pic_remap(int pic1_start, int pic2_start);
|
||||
|
||||
#endif
|
1
printf.c
1
printf.c
|
@ -866,6 +866,7 @@ int printf_(const char* format, ...)
|
|||
char buffer[1];
|
||||
const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
|
||||
va_end(va);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
5
printf.h
5
printf.h
|
@ -32,10 +32,6 @@
|
|||
#ifndef _PRINTF_H_
|
||||
#define _PRINTF_H_
|
||||
|
||||
#define PRINTF_DISABLE_SUPPORT_FLOAT
|
||||
#define PRINTF_DISABLE_SUPPORT_EXPONENTIAL
|
||||
#define PRINTF_DISABLE_SUPPORT_LONG_LONG
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
@ -62,6 +58,7 @@ void _putchar(char character);
|
|||
* \return The number of characters that are written into the array, not counting the terminating null character
|
||||
*/
|
||||
#define printf printf_
|
||||
//int printf_(const char* format, ...);
|
||||
int printf_(const char* format, ...);
|
||||
|
||||
|
||||
|
|
432
ps2.c
Normal file
432
ps2.c
Normal file
|
@ -0,0 +1,432 @@
|
|||
#include <ps2.h>
|
||||
#include <io.h>
|
||||
#include <pic8259.h>
|
||||
#include <vga.h>
|
||||
#include <printf.h>
|
||||
#include <strcmp.h>
|
||||
|
||||
#define cpu_relax asm volatile ("pause" ::);
|
||||
|
||||
char shell_buffer[512];
|
||||
int shell_index;
|
||||
|
||||
// TODO: Support dual ports
|
||||
void initialize_8042ps2() {
|
||||
// disable ps/2 ports so nothing gets messed up
|
||||
outportb(PS2_8042_COMMAND, 0xAD);
|
||||
outportb(PS2_8042_COMMAND, 0xA7);
|
||||
|
||||
// flush output buffer
|
||||
inportb(PS2_8042_DATA);
|
||||
|
||||
// get config byte
|
||||
outportb(PS2_8042_COMMAND, 0x20);
|
||||
uint8_t config = inportb(0x60);
|
||||
// disable IRQs and translation, also make sure 0 vals are 0
|
||||
config &= 0b00110100;
|
||||
// write our config byte back
|
||||
outportb(PS2_8042_COMMAND, 0x60);
|
||||
while (inportb(PS2_8042_STATUS) & 0x2) cpu_relax;
|
||||
outportb(PS2_8042_DATA, config);
|
||||
|
||||
// perform self test
|
||||
outportb(PS2_8042_COMMAND, 0xAA);
|
||||
while (!(inportb(PS2_8042_STATUS) & 0x1)) cpu_relax;
|
||||
uint8_t result = inportb(PS2_8042_DATA);
|
||||
if (result == 0x55) printf("Passed PS/2 self test!\n");
|
||||
else printf("Failed!\n");
|
||||
|
||||
// perform port test
|
||||
outportb(PS2_8042_COMMAND, 0xAB);
|
||||
while (!(inportb(PS2_8042_STATUS) & 0x1)) cpu_relax;
|
||||
result = inportb(PS2_8042_DATA);
|
||||
if (!result) printf("Passed PS/2 port 1 test!!\n");
|
||||
else printf("Failed!\n");
|
||||
|
||||
// enable ps/2 port
|
||||
outportb(PS2_8042_COMMAND, 0xAE);
|
||||
|
||||
// enable IRQ 1
|
||||
config = 0b01100101;
|
||||
outportb(PS2_8042_COMMAND, 0x60);
|
||||
while (inportb(PS2_8042_STATUS) & 0x2) cpu_relax;
|
||||
outportb(PS2_8042_DATA, config);
|
||||
}
|
||||
|
||||
void ps2_handler() {
|
||||
uint8_t scancode = inportb(0x60);
|
||||
|
||||
if (scancode > 0x80) goto eoi;
|
||||
|
||||
switch(scancode) {
|
||||
case KBD_ESC:
|
||||
break;
|
||||
case KBD_1:
|
||||
_putchar('1');
|
||||
shell_buffer[shell_index++] = '1';
|
||||
break;
|
||||
case KBD_2:
|
||||
_putchar('2');
|
||||
shell_buffer[shell_index++] = '2';
|
||||
break;
|
||||
case KBD_3:
|
||||
_putchar('3');
|
||||
shell_buffer[shell_index++] = '3';
|
||||
break;
|
||||
case KBD_4:
|
||||
_putchar('4');
|
||||
shell_buffer[shell_index++] = '4';
|
||||
break;
|
||||
case KBD_5:
|
||||
_putchar('5');
|
||||
shell_buffer[shell_index++] = '5';
|
||||
break;
|
||||
case KBD_6:
|
||||
_putchar('6');
|
||||
shell_buffer[shell_index++] = '6';
|
||||
break;
|
||||
case KBD_7:
|
||||
_putchar('7');
|
||||
shell_buffer[shell_index++] = '7';
|
||||
break;
|
||||
case KBD_8:
|
||||
_putchar('8');
|
||||
shell_buffer[shell_index++] = '8';
|
||||
break;
|
||||
case KBD_9:
|
||||
_putchar('9');
|
||||
shell_buffer[shell_index++] = '9';
|
||||
break;
|
||||
case KBD_0:
|
||||
_putchar('0');
|
||||
shell_buffer[shell_index++] = '0';
|
||||
break;
|
||||
case KBD_MINUS:
|
||||
_putchar('-');
|
||||
shell_buffer[shell_index++] = '-';
|
||||
break;
|
||||
case KBD_EQUALS:
|
||||
_putchar('=');
|
||||
shell_buffer[shell_index++] = '=';
|
||||
break;
|
||||
case KBD_BACKSPACE:
|
||||
if (shell_index > 0) {
|
||||
popchar();
|
||||
shell_buffer[--shell_index] = 0;
|
||||
}
|
||||
break;
|
||||
case KBD_TAB:
|
||||
break;
|
||||
case KBD_Q:
|
||||
_putchar('Q');
|
||||
shell_buffer[shell_index++] = 'Q';
|
||||
break;
|
||||
case KBD_W:
|
||||
_putchar('W');
|
||||
shell_buffer[shell_index++] = 'W';
|
||||
break;
|
||||
case KBD_E:
|
||||
_putchar('E');
|
||||
shell_buffer[shell_index++] = 'E';
|
||||
break;
|
||||
case KBD_R:
|
||||
_putchar('R');
|
||||
shell_buffer[shell_index++] = 'R';
|
||||
break;
|
||||
case KBD_T:
|
||||
_putchar('T');
|
||||
shell_buffer[shell_index++] = 'T';
|
||||
break;
|
||||
case KBD_Y:
|
||||
_putchar('Y');
|
||||
shell_buffer[shell_index++] = 'Y';
|
||||
break;
|
||||
case KBD_U:
|
||||
_putchar('U');
|
||||
shell_buffer[shell_index++] = 'U';
|
||||
break;
|
||||
case KBD_I:
|
||||
_putchar('I');
|
||||
shell_buffer[shell_index++] = 'I';
|
||||
break;
|
||||
case KBD_O:
|
||||
_putchar('O');
|
||||
shell_buffer[shell_index++] = 'O';
|
||||
break;
|
||||
case KBD_P:
|
||||
_putchar('P');
|
||||
shell_buffer[shell_index++] = 'P';
|
||||
break;
|
||||
case KBD_LBRACKET:
|
||||
_putchar('[');
|
||||
shell_buffer[shell_index++] = '[';
|
||||
break;
|
||||
case KBD_RBRACKET:
|
||||
_putchar(']');
|
||||
shell_buffer[shell_index++] = ']';
|
||||
break;
|
||||
case KBD_ENTER:
|
||||
printf("\n");
|
||||
if (strcmp(shell_buffer, "PINGAS") == 0) {
|
||||
printf("andew pingas detected");
|
||||
} else if (strcmp(shell_buffer, "HELLO") == 0) {
|
||||
printf("hi");
|
||||
} else {
|
||||
printf("unknown command '%s' idiot", shell_buffer);
|
||||
}
|
||||
for (int i = 0; i < 512; i++) {
|
||||
shell_buffer[i] = 0;
|
||||
}
|
||||
shell_index = 0;
|
||||
printf("\n$ ");
|
||||
break;
|
||||
case KBD_LCTRL:
|
||||
// Handle left control key
|
||||
break;
|
||||
case KBD_A:
|
||||
_putchar('A');
|
||||
shell_buffer[shell_index++] = 'A';
|
||||
break;
|
||||
case KBD_S:
|
||||
_putchar('S');
|
||||
shell_buffer[shell_index++] = 'S';
|
||||
break;
|
||||
case KBD_D:
|
||||
_putchar('D');
|
||||
shell_buffer[shell_index++] = 'D';
|
||||
break;
|
||||
case KBD_F:
|
||||
_putchar('F');
|
||||
shell_buffer[shell_index++] = 'F';
|
||||
break;
|
||||
case KBD_G:
|
||||
_putchar('G');
|
||||
shell_buffer[shell_index++] = 'G';
|
||||
break;
|
||||
case KBD_H:
|
||||
_putchar('H');
|
||||
shell_buffer[shell_index++] = 'H';
|
||||
break;
|
||||
case KBD_J:
|
||||
_putchar('J');
|
||||
shell_buffer[shell_index++] = 'J';
|
||||
break;
|
||||
case KBD_K:
|
||||
_putchar('K');
|
||||
shell_buffer[shell_index++] = 'K';
|
||||
break;
|
||||
case KBD_L:
|
||||
_putchar('L');
|
||||
shell_buffer[shell_index++] = 'L';
|
||||
break;
|
||||
case KBD_SEMICOLON:
|
||||
_putchar(';');
|
||||
shell_buffer[shell_index++] = ';';
|
||||
break;
|
||||
case KBD_APOSTROPHE:
|
||||
_putchar('\'');
|
||||
shell_buffer[shell_index++] = '\'';
|
||||
break;
|
||||
case KBD_GRAVE:
|
||||
_putchar('`');
|
||||
shell_buffer[shell_index++] = '`';
|
||||
break;
|
||||
case KBD_LSHIFT:
|
||||
// Handle left shift key
|
||||
break;
|
||||
case KBD_BACKSLASH:
|
||||
_putchar('\\');
|
||||
shell_buffer[shell_index++] = '\\';
|
||||
break;
|
||||
case KBD_Z:
|
||||
_putchar('Z');
|
||||
shell_buffer[shell_index++] = 'Z';
|
||||
break;
|
||||
case KBD_X:
|
||||
_putchar('X');
|
||||
shell_buffer[shell_index++] = 'X';
|
||||
break;
|
||||
case KBD_C:
|
||||
_putchar('C');
|
||||
shell_buffer[shell_index++] = 'C';
|
||||
break;
|
||||
case KBD_V:
|
||||
_putchar('V');
|
||||
shell_buffer[shell_index++] = 'V';
|
||||
break;
|
||||
case KBD_B:
|
||||
_putchar('B');
|
||||
shell_buffer[shell_index++] = 'B';
|
||||
break;
|
||||
case KBD_N:
|
||||
_putchar('N');
|
||||
shell_buffer[shell_index++] = 'N';
|
||||
break;
|
||||
case KBD_M:
|
||||
_putchar('M');
|
||||
shell_buffer[shell_index++] = 'M';
|
||||
break;
|
||||
case KBD_COMMA:
|
||||
_putchar(',');
|
||||
shell_buffer[shell_index++] = ',';
|
||||
break;
|
||||
case KBD_PERIOD:
|
||||
_putchar('.');
|
||||
shell_buffer[shell_index++] = '.';
|
||||
break;
|
||||
case KBD_SLASH:
|
||||
_putchar('/');
|
||||
shell_buffer[shell_index++] = '/';
|
||||
break;
|
||||
case KBD_RSHIFT:
|
||||
// Handle right shift key
|
||||
break;
|
||||
case KBD_PRINTSCREEN:
|
||||
// Handle print screen key
|
||||
break;
|
||||
case KBD_LALT:
|
||||
// Handle left alt key
|
||||
break;
|
||||
case KBD_SPACE:
|
||||
_putchar(' ');
|
||||
shell_buffer[shell_index++] = ' ';
|
||||
break;
|
||||
case KBD_CAPSLOCK:
|
||||
// Handle caps lock key
|
||||
break;
|
||||
case KBD_F1:
|
||||
// Handle F1 key
|
||||
break;
|
||||
case KBD_F2:
|
||||
// Handle F2 key
|
||||
break;
|
||||
case KBD_F3:
|
||||
// Handle F3 key
|
||||
break;
|
||||
case KBD_F4:
|
||||
// Handle F4 key
|
||||
break;
|
||||
case KBD_F5:
|
||||
// Handle F5 key
|
||||
break;
|
||||
case KBD_F6:
|
||||
// Handle F6 key
|
||||
break;
|
||||
case KBD_F7:
|
||||
// Handle F7 key
|
||||
break;
|
||||
case KBD_F8:
|
||||
// Handle F8 key
|
||||
break;
|
||||
case KBD_F9:
|
||||
// Handle F9 key
|
||||
break;
|
||||
case KBD_F10:
|
||||
// Handle F10 key
|
||||
break;
|
||||
case KBD_NUMLOCK:
|
||||
// Handle num lock key
|
||||
break;
|
||||
case KBD_SCROLLLOCK:
|
||||
// Handle scroll lock key
|
||||
break;
|
||||
case KBD_HOME:
|
||||
// Handle home key
|
||||
break;
|
||||
case KBD_UP:
|
||||
// Handle up arrow key
|
||||
break;
|
||||
case KBD_PAGEUP:
|
||||
// Handle page up key
|
||||
break;
|
||||
case KBD_KP_MINUS:
|
||||
// Handle keypad minus key
|
||||
break;
|
||||
case KBD_LEFT:
|
||||
// Handle left arrow key
|
||||
break;
|
||||
case KBD_KP_5:
|
||||
// Handle keypad 5 key
|
||||
break;
|
||||
case KBD_RIGHT:
|
||||
// Handle right arrow key
|
||||
break;
|
||||
case KBD_KP_PLUS:
|
||||
// Handle keypad plus key
|
||||
break;
|
||||
case KBD_END:
|
||||
// Handle end key
|
||||
break;
|
||||
case KBD_DOWN:
|
||||
// Handle down arrow key
|
||||
break;
|
||||
case KBD_PAGEDOWN:
|
||||
// Handle page down key
|
||||
break;
|
||||
case KBD_INSERT:
|
||||
// Handle insert key
|
||||
break;
|
||||
case KBD_DELETE:
|
||||
// Handle delete key
|
||||
break;
|
||||
case KBD_SYSRQ:
|
||||
// Handle system request key
|
||||
break;
|
||||
case KBD_F11:
|
||||
// Handle F11 key
|
||||
break;
|
||||
case KBD_F12:
|
||||
// Handle F12 key
|
||||
break;
|
||||
case KBD_KP_ENTER:
|
||||
break;
|
||||
case KBD_RCTRL:
|
||||
// Handle right control key
|
||||
break;
|
||||
case KBD_KP_DIVIDE:
|
||||
// Handle keypad divide key
|
||||
break;
|
||||
case KBD_PRINTSCREEN2:
|
||||
// Handle print screen key (alternate)
|
||||
break;
|
||||
case KBD_RALT:
|
||||
// Handle right alt key
|
||||
break;
|
||||
case KBD_HOME2:
|
||||
// Handle home key (alternate)
|
||||
break;
|
||||
case KBD_UP2:
|
||||
// Handle up arrow key (alternate)
|
||||
break;
|
||||
case KBD_PAGEUP2:
|
||||
// Handle page up key (alternate)
|
||||
break;
|
||||
case KBD_LEFT2:
|
||||
// Handle left arrow key (alternate)
|
||||
break;
|
||||
case KBD_RIGHT2:
|
||||
// Handle right arrow key (alternate)
|
||||
break;
|
||||
case KBD_END2:
|
||||
// Handle end key (alternate)
|
||||
break;
|
||||
case KBD_DOWN2:
|
||||
// Handle down arrow key (alternate)
|
||||
break;
|
||||
case KBD_PAGEDOWN2:
|
||||
// Handle page down key (alternate)
|
||||
break;
|
||||
case KBD_INSERT2:
|
||||
// Handle insert key (alternate)
|
||||
break;
|
||||
case KBD_DELETE2:
|
||||
// Handle delete key (alternate)
|
||||
break;
|
||||
default:
|
||||
// Handle unknown scan codes
|
||||
printf("Unknown scan code\n");
|
||||
break;
|
||||
}
|
||||
eoi:
|
||||
pic_send_eoi(1);
|
||||
}
|
115
ps2.h
Normal file
115
ps2.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
#ifndef ANDEWOS_PS2_H
|
||||
#define ANDEWOS_PS2_H
|
||||
|
||||
#define PS2_8042_DATA 0x60
|
||||
#define PS2_8042_STATUS 0x64
|
||||
#define PS2_8042_COMMAND 0x64
|
||||
|
||||
// SCANCODES
|
||||
#define KBD_ESC 0x01
|
||||
#define KBD_1 0x02
|
||||
#define KBD_2 0x03
|
||||
#define KBD_3 0x04
|
||||
#define KBD_4 0x05
|
||||
#define KBD_5 0x06
|
||||
#define KBD_6 0x07
|
||||
#define KBD_7 0x08
|
||||
#define KBD_8 0x09
|
||||
#define KBD_9 0x0A
|
||||
#define KBD_0 0x0B
|
||||
#define KBD_MINUS 0x0C
|
||||
#define KBD_EQUALS 0x0D
|
||||
#define KBD_BACKSPACE 0x0E
|
||||
#define KBD_TAB 0x0F
|
||||
#define KBD_Q 0x10
|
||||
#define KBD_W 0x11
|
||||
#define KBD_E 0x12
|
||||
#define KBD_R 0x13
|
||||
#define KBD_T 0x14
|
||||
#define KBD_Y 0x15
|
||||
#define KBD_U 0x16
|
||||
#define KBD_I 0x17
|
||||
#define KBD_O 0x18
|
||||
#define KBD_P 0x19
|
||||
#define KBD_LBRACKET 0x1A
|
||||
#define KBD_RBRACKET 0x1B
|
||||
#define KBD_ENTER 0x1C
|
||||
#define KBD_LCTRL 0x1D
|
||||
#define KBD_A 0x1E
|
||||
#define KBD_S 0x1F
|
||||
#define KBD_D 0x20
|
||||
#define KBD_F 0x21
|
||||
#define KBD_G 0x22
|
||||
#define KBD_H 0x23
|
||||
#define KBD_J 0x24
|
||||
#define KBD_K 0x25
|
||||
#define KBD_L 0x26
|
||||
#define KBD_SEMICOLON 0x27
|
||||
#define KBD_APOSTROPHE 0x28
|
||||
#define KBD_GRAVE 0x29
|
||||
#define KBD_LSHIFT 0x2A
|
||||
#define KBD_BACKSLASH 0x2B
|
||||
#define KBD_Z 0x2C
|
||||
#define KBD_X 0x2D
|
||||
#define KBD_C 0x2E
|
||||
#define KBD_V 0x2F
|
||||
#define KBD_B 0x30
|
||||
#define KBD_N 0x31
|
||||
#define KBD_M 0x32
|
||||
#define KBD_COMMA 0x33
|
||||
#define KBD_PERIOD 0x34
|
||||
#define KBD_SLASH 0x35
|
||||
#define KBD_RSHIFT 0x36
|
||||
#define KBD_PRINTSCREEN 0x37
|
||||
#define KBD_LALT 0x38
|
||||
#define KBD_SPACE 0x39
|
||||
#define KBD_CAPSLOCK 0x3A
|
||||
#define KBD_F1 0x3B
|
||||
#define KBD_F2 0x3C
|
||||
#define KBD_F3 0x3D
|
||||
#define KBD_F4 0x3E
|
||||
#define KBD_F5 0x3F
|
||||
#define KBD_F6 0x40
|
||||
#define KBD_F7 0x41
|
||||
#define KBD_F8 0x42
|
||||
#define KBD_F9 0x43
|
||||
#define KBD_F10 0x44
|
||||
#define KBD_NUMLOCK 0x45
|
||||
#define KBD_SCROLLLOCK 0x46
|
||||
#define KBD_HOME 0x47
|
||||
#define KBD_UP 0x48
|
||||
#define KBD_PAGEUP 0x49
|
||||
#define KBD_KP_MINUS 0x4A
|
||||
#define KBD_LEFT 0x4B
|
||||
#define KBD_KP_5 0x4C
|
||||
#define KBD_RIGHT 0x4D
|
||||
#define KBD_KP_PLUS 0x4E
|
||||
#define KBD_END 0x4F
|
||||
#define KBD_DOWN 0x50
|
||||
#define KBD_PAGEDOWN 0x51
|
||||
#define KBD_INSERT 0x52
|
||||
#define KBD_DELETE 0x53
|
||||
#define KBD_SYSRQ 0x54
|
||||
#define KBD_F11 0x57
|
||||
#define KBD_F12 0x58
|
||||
#define KBD_KP_ENTER 0x9C
|
||||
#define KBD_RCTRL 0x9D
|
||||
#define KBD_KP_DIVIDE 0xB5
|
||||
#define KBD_PRINTSCREEN2 0xB7
|
||||
#define KBD_RALT 0xB8
|
||||
#define KBD_HOME2 0xC7
|
||||
#define KBD_UP2 0xC8
|
||||
#define KBD_PAGEUP2 0xC9
|
||||
#define KBD_LEFT2 0xCB
|
||||
#define KBD_RIGHT2 0xCD
|
||||
#define KBD_END2 0xCF
|
||||
#define KBD_DOWN2 0xD0
|
||||
#define KBD_PAGEDOWN2 0xD1
|
||||
#define KBD_INSERT2 0xD2
|
||||
#define KBD_DELETE2 0xD3
|
||||
|
||||
void initialize_8042ps2();
|
||||
extern void ps2_isr();
|
||||
void ps2_handler();
|
||||
|
||||
#endif
|
10
strcmp.c
10
strcmp.c
|
@ -1,5 +1,15 @@
|
|||
#include <strcmp.h>
|
||||
|
||||
int strcmp(const char *s1, const char *s2)
|
||||
{
|
||||
while(*s1 && (*s1 == *s2))
|
||||
{
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
return *(const unsigned char*)s1 - *(const unsigned char*)s2;
|
||||
}
|
||||
|
||||
int strncmp( const char * s1, const char * s2, int n ) {
|
||||
while ( n && *s1 && ( *s1 == *s2 ) )
|
||||
{
|
||||
|
|
3
strcmp.h
3
strcmp.h
|
@ -1,6 +1,7 @@
|
|||
#ifndef RK_STRCMP_
|
||||
#define RK_STRCMP_
|
||||
|
||||
int strncmp( const char * s1, const char * s2, int n );
|
||||
int strcmp(const char *s1, const char *s2);
|
||||
int strncmp(const char *s1, const char *s2, int n);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue
Block a user