Provided Files
This commit is contained in:
parent
394fdf57ae
commit
46d42d4551
BIN
Part1/.DS_Store
vendored
BIN
Part1/.DS_Store
vendored
Binary file not shown.
BIN
Part2/.DS_Store
vendored
BIN
Part2/.DS_Store
vendored
Binary file not shown.
BIN
Part2/A4/.DS_Store
vendored
BIN
Part2/A4/.DS_Store
vendored
Binary file not shown.
BIN
Part2/A4/01_gdb/.DS_Store
vendored
BIN
Part2/A4/01_gdb/.DS_Store
vendored
Binary file not shown.
BIN
Part2/A4/03_strings/.DS_Store
vendored
Normal file
BIN
Part2/A4/03_strings/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
Part2/A4/04_scroll/.DS_Store
vendored
BIN
Part2/A4/04_scroll/.DS_Store
vendored
Binary file not shown.
BIN
Part2/A4/06_irq/.DS_Store
vendored
BIN
Part2/A4/06_irq/.DS_Store
vendored
Binary file not shown.
BIN
Part2/A4/07_timers/.DS_Store
vendored
BIN
Part2/A4/07_timers/.DS_Store
vendored
Binary file not shown.
BIN
Part3/.DS_Store
vendored
Normal file
BIN
Part3/.DS_Store
vendored
Normal file
Binary file not shown.
51
Part3/09_memory/Makefile
Normal file
51
Part3/09_memory/Makefile
Normal file
@ -0,0 +1,51 @@
|
||||
C_SOURCES = $(wildcard kernel/*.c drivers/*.c cpu/*.c libc/*.c)
|
||||
HEADERS = $(wildcard kernel/*.h drivers/*.h cpu/*.h libc/*.h)
|
||||
# Nice syntax for file extension replacement
|
||||
OBJ = ${C_SOURCES:.c=.o cpu/interrupt.o}
|
||||
|
||||
# Change this if your cross-compiler is somewhere else
|
||||
CC = /usr/bin/gcc
|
||||
GDB = /usr/bin/gdb
|
||||
# -g: Use debugging symbols in gcc
|
||||
CFLAGS = -g -m32 -fno-pie -ffreestanding -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -Wall -Wextra -Werror
|
||||
|
||||
# First rule is run by default
|
||||
os-image.bin: boot/bootsect.bin kernel.bin
|
||||
cat $^ > os-image.bin
|
||||
|
||||
# '--oformat binary' deletes all symbols as a collateral, so we don't need
|
||||
# to 'strip' them manually on this case
|
||||
kernel.bin: boot/kernel_entry.o ${OBJ}
|
||||
/usr/bin/ld -m elf_i386 -o $@ -Ttext 0x1000 $^ --oformat binary
|
||||
|
||||
# Used for debugging purposes
|
||||
kernel.elf: boot/kernel_entry.o ${OBJ}
|
||||
/usr/bin/ld -m elf_i386 -o $@ -Ttext 0x1000 $^
|
||||
|
||||
run: os-image.bin
|
||||
qemu-system-i386 -vga std -drive file=$<,index=0,if=floppy,format=raw
|
||||
|
||||
#-s means start server at TCP:1234
|
||||
curses: os-image.bin
|
||||
qemu-system-i386 -curses -s -drive file=$<,index=0,if=floppy,format=raw
|
||||
# qemu-system-i386 -curses -s -drive file=os-image.bin,index=0,if=floppy,format=raw -d guest_errors,int
|
||||
|
||||
# Open the connection to qemu and load our kernel-object file with symbols
|
||||
debug: os-image.bin kernel.elf
|
||||
qemu-system-i386 -s -fda os-image.bin -d guest_errors,int &
|
||||
${GDB} -ex "target remote localhost:1234" -ex "symbol-file kernel.elf"
|
||||
|
||||
# Generic rules for wildcards
|
||||
# To make an object, always compile from its .c
|
||||
%.o: %.c ${HEADERS}
|
||||
${CC} ${CFLAGS} -c $< -o $@
|
||||
|
||||
%.o: %.asm
|
||||
nasm $< -f elf -o $@
|
||||
|
||||
%.bin: %.asm
|
||||
nasm $< -f bin -o $@
|
||||
|
||||
clean:
|
||||
rm -rf *.bin *.dis *.o os-image.bin *.elf
|
||||
rm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o cpu/*.o libc/*.o
|
24
Part3/09_memory/README.md
Normal file
24
Part3/09_memory/README.md
Normal file
@ -0,0 +1,24 @@
|
||||
*Concepts you may want to Google beforehand: malloc*
|
||||
|
||||
**Goal: Implement a memory allocator**
|
||||
|
||||
We will add a kernel memory allocator to `libc/mem.c`. It is
|
||||
implemented as a simple pointer to free memory, which keeps
|
||||
growing.
|
||||
|
||||
The `kmalloc()` function can be used to request an aligned page,
|
||||
and it will also return the real, physical address, for later use.
|
||||
|
||||
We'll change the `kernel.c` leaving all the "shell" code there,
|
||||
Let's just try out the new `kmalloc()`, and check out that
|
||||
our first page starts at 0x10000 (as hardcoded on `mem.c`) and
|
||||
subsequent `kmalloc()`'s produce a new address which is
|
||||
aligned 4096 bytes or 0x1000 from the previous one.
|
||||
|
||||
Note that we added a new `strings.c:hex_to_ascii()` for
|
||||
nicer printing of hex numbers.
|
||||
|
||||
Another cosmetic modification is to rename `types.c` to
|
||||
`type.c` for language consistency.
|
||||
|
||||
The rest of the files are unchanged from last lesson.
|
52
Part3/09_memory/_Makefile
Normal file
52
Part3/09_memory/_Makefile
Normal file
@ -0,0 +1,52 @@
|
||||
C_SOURCES = $(wildcard kernel/*.c drivers/*.c cpu/*.c libc/*.c)
|
||||
HEADERS = $(wildcard kernel/*.h drivers/*.h cpu/*.h libc/*.h)
|
||||
# Nice syntax for file extension replacement
|
||||
OBJ = ${C_SOURCES:.c=.o cpu/interrupt.o}
|
||||
|
||||
# Change this if your cross-compiler is somewhere else
|
||||
CC = /usr/bin/gcc
|
||||
GDB = /usr/bin/gdb
|
||||
# -g: Use debugging symbols in gcc
|
||||
CFLAGS = -g -m32 -fno-pie -ffreestanding -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -Wall -Wextra -Werror
|
||||
|
||||
# First rule is run by default
|
||||
os-image.bin: boot/bootsect.bin kernel.bin
|
||||
cat $^ > os-image.bin
|
||||
|
||||
# '--oformat binary' deletes all symbols as a collateral, so we don't need
|
||||
# to 'strip' them manually on this case
|
||||
kernel.bin: boot/kernel_entry.o ${OBJ}
|
||||
/usr/bin/ld -m elf_i386 -o $@ -Ttext 0x1000 $^ --oformat binary
|
||||
|
||||
# Used for debugging purposes
|
||||
kernel.elf: boot/kernel_entry.o ${OBJ}
|
||||
/usr/bin/ld -m elf_i386 -o $@ -Ttext 0x1000 $^
|
||||
|
||||
run: os-image.bin
|
||||
qemu-system-i386 -vga std -drive file=$<,index=0,if=floppy,format=raw
|
||||
|
||||
#-s means start server at TCP:1234
|
||||
curses: os-image.bin
|
||||
qemu-system-i386 -curses -s -drive file=$<,index=0,if=floppy,format=raw
|
||||
|
||||
# Open the connection to qemu and load our kernel-object file with symbols
|
||||
debug: os-image.bin kernel.elf
|
||||
qemu-system-i386 -curses -s -drive file=os-image.bin,index=0,if=floppy,format=raw -d guest_errors,int
|
||||
|
||||
gdb:
|
||||
${GDB} -ex "target remote localhost:1234" -ex "symbol-file kernel.elf"
|
||||
|
||||
# Generic rules for wildcards
|
||||
# To make an object, always compile from its .c
|
||||
%.o: %.c ${HEADERS}
|
||||
${CC} ${CFLAGS} -c $< -o $@
|
||||
|
||||
%.o: %.asm
|
||||
nasm $< -f elf -o $@
|
||||
|
||||
%.bin: %.asm
|
||||
nasm $< -f bin -o $@
|
||||
|
||||
clean:
|
||||
rm -rf *.bin *.dis *.o os-image.bin *.elf
|
||||
rm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o cpu/*.o libc/*.o
|
26
Part3/09_memory/boot/32bit_print.asm
Normal file
26
Part3/09_memory/boot/32bit_print.asm
Normal file
@ -0,0 +1,26 @@
|
||||
[bits 32] ; using 32-bit protected mode
|
||||
|
||||
; this is how constants are defined
|
||||
VIDEO_MEMORY equ 0xb8000
|
||||
WHITE_OB_BLACK equ 0x0f ; the color byte for each character
|
||||
|
||||
print_string_pm:
|
||||
pusha
|
||||
mov edx, VIDEO_MEMORY
|
||||
|
||||
print_string_pm_loop:
|
||||
mov al, [ebx] ; [ebx] is the address of our character
|
||||
mov ah, WHITE_OB_BLACK
|
||||
|
||||
cmp al, 0 ; check if end of string
|
||||
je print_string_pm_done
|
||||
|
||||
mov [edx], ax ; store character + attribute in video memory
|
||||
add ebx, 1 ; next char
|
||||
add edx, 2 ; next video memory position
|
||||
|
||||
jmp print_string_pm_loop
|
||||
|
||||
print_string_pm_done:
|
||||
popa
|
||||
ret
|
51
Part3/09_memory/boot/bootsect.asm
Normal file
51
Part3/09_memory/boot/bootsect.asm
Normal file
@ -0,0 +1,51 @@
|
||||
; Identical to lesson 13's boot sector, but the %included files have new paths
|
||||
[org 0x7c00]
|
||||
KERNEL_OFFSET equ 0x1000 ; The same one we used when linking the kernel
|
||||
|
||||
mov [BOOT_DRIVE], dl ; Remember that the BIOS sets us the boot drive in 'dl' on boot
|
||||
mov bp, 0x9000
|
||||
mov sp, bp
|
||||
|
||||
mov bx, MSG_REAL_MODE
|
||||
call print
|
||||
call print_nl
|
||||
|
||||
call load_kernel ; read the kernel from disk
|
||||
call switch_to_pm ; disable interrupts, load GDT, etc. Finally jumps to 'BEGIN_PM'
|
||||
jmp $ ; Never executed
|
||||
|
||||
%include "boot/print.asm"
|
||||
%include "boot/print_hex.asm"
|
||||
%include "boot/disk.asm"
|
||||
%include "boot/gdt.asm"
|
||||
%include "boot/32bit_print.asm"
|
||||
%include "boot/switch_pm.asm"
|
||||
|
||||
[bits 16]
|
||||
load_kernel:
|
||||
mov bx, MSG_LOAD_KERNEL
|
||||
call print
|
||||
call print_nl
|
||||
|
||||
mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000
|
||||
mov dh, 32 ; Our future kernel will be larger, make this big
|
||||
mov dl, [BOOT_DRIVE]
|
||||
call disk_load
|
||||
ret
|
||||
|
||||
[bits 32]
|
||||
BEGIN_PM:
|
||||
mov ebx, MSG_PROT_MODE
|
||||
call print_string_pm
|
||||
call KERNEL_OFFSET ; Give control to the kernel
|
||||
jmp $ ; Stay here when the kernel returns control to us (if ever)
|
||||
|
||||
|
||||
BOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten
|
||||
MSG_REAL_MODE db "Started in 16-bit Real Mode", 0
|
||||
MSG_PROT_MODE db "Landed in 32-bit Protected Mode", 0
|
||||
MSG_LOAD_KERNEL db "Loading kernel into memory", 0
|
||||
|
||||
; padding
|
||||
times 510 - ($-$$) db 0
|
||||
dw 0xaa55
|
46
Part3/09_memory/boot/disk.asm
Normal file
46
Part3/09_memory/boot/disk.asm
Normal file
@ -0,0 +1,46 @@
|
||||
; load 'dh' sectors from drive 'dl' into ES:BX
|
||||
disk_load:
|
||||
pusha
|
||||
; reading from disk requires setting specific values in all registers
|
||||
; so we will overwrite our input parameters from 'dx'. Let's save it
|
||||
; to the stack for later use.
|
||||
push dx
|
||||
|
||||
mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read'
|
||||
mov al, dh ; al <- number of sectors to read (0x01 .. 0x80)
|
||||
mov cl, 0x02 ; cl <- sector (0x01 .. 0x11)
|
||||
; 0x01 is our boot sector, 0x02 is the first 'available' sector
|
||||
mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl')
|
||||
; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS
|
||||
; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2)
|
||||
mov dh, 0x00 ; dh <- head number (0x0 .. 0xF)
|
||||
|
||||
; [es:bx] <- pointer to buffer where the data will be stored
|
||||
; caller sets it up for us, and it is actually the standard location for int 13h
|
||||
int 0x13 ; BIOS interrupt
|
||||
jc disk_error ; if error (stored in the carry bit)
|
||||
|
||||
pop dx
|
||||
cmp al, dh ; BIOS also sets 'al' to the # of sectors read. Compare it.
|
||||
jne sectors_error
|
||||
popa
|
||||
ret
|
||||
|
||||
|
||||
disk_error:
|
||||
mov bx, DISK_ERROR
|
||||
call print
|
||||
call print_nl
|
||||
mov dh, ah ; ah = error code, dl = disk drive that dropped the error
|
||||
call print_hex ; check out the code at http://stanislavs.org/helppc/int_13-1.html
|
||||
jmp disk_loop
|
||||
|
||||
sectors_error:
|
||||
mov bx, SECTORS_ERROR
|
||||
call print
|
||||
|
||||
disk_loop:
|
||||
jmp $
|
||||
|
||||
DISK_ERROR: db "Disk read error", 0
|
||||
SECTORS_ERROR: db "Incorrect number of sectors read", 0
|
35
Part3/09_memory/boot/gdt.asm
Normal file
35
Part3/09_memory/boot/gdt.asm
Normal file
@ -0,0 +1,35 @@
|
||||
gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps
|
||||
; the GDT starts with a null 8-byte
|
||||
dd 0x0 ; 4 byte
|
||||
dd 0x0 ; 4 byte
|
||||
|
||||
; GDT for code segment. base = 0x00000000, length = 0xfffff
|
||||
; for flags, refer to os-dev.pdf document, page 36
|
||||
gdt_code:
|
||||
dw 0xffff ; segment length, bits 0-15
|
||||
dw 0x0 ; segment base, bits 0-15
|
||||
db 0x0 ; segment base, bits 16-23
|
||||
db 10011010b ; flags (8 bits)
|
||||
db 11001111b ; flags (4 bits) + segment length, bits 16-19
|
||||
db 0x0 ; segment base, bits 24-31
|
||||
|
||||
; GDT for data segment. base and length identical to code segment
|
||||
; some flags changed, again, refer to os-dev.pdf
|
||||
gdt_data:
|
||||
dw 0xffff
|
||||
dw 0x0
|
||||
db 0x0
|
||||
db 10010010b
|
||||
db 11001111b
|
||||
db 0x0
|
||||
|
||||
gdt_end:
|
||||
|
||||
; GDT descriptor
|
||||
gdt_descriptor:
|
||||
dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size
|
||||
dd gdt_start ; address (32 bit)
|
||||
|
||||
; define some constants for later use
|
||||
CODE_SEG equ gdt_code - gdt_start
|
||||
DATA_SEG equ gdt_data - gdt_start
|
4
Part3/09_memory/boot/kernel_entry.asm
Normal file
4
Part3/09_memory/boot/kernel_entry.asm
Normal file
@ -0,0 +1,4 @@
|
||||
[bits 32]
|
||||
[extern _start] ; Define calling point. Must have same name as kernel.c '_start' function
|
||||
call _start ; Calls the C function. The linker will know where it is placed in memory
|
||||
jmp $
|
37
Part3/09_memory/boot/print.asm
Normal file
37
Part3/09_memory/boot/print.asm
Normal file
@ -0,0 +1,37 @@
|
||||
print:
|
||||
pusha
|
||||
|
||||
; keep this in mind:
|
||||
; while (string[i] != 0) { print string[i]; i++ }
|
||||
|
||||
; the comparison for string end (null byte)
|
||||
start:
|
||||
mov al, [bx] ; 'bx' is the base address for the string
|
||||
cmp al, 0
|
||||
je done
|
||||
|
||||
; the part where we print with the BIOS help
|
||||
mov ah, 0x0e
|
||||
int 0x10 ; 'al' already contains the char
|
||||
|
||||
; increment pointer and do next loop
|
||||
add bx, 1
|
||||
jmp start
|
||||
|
||||
done:
|
||||
popa
|
||||
ret
|
||||
|
||||
|
||||
|
||||
print_nl:
|
||||
pusha
|
||||
|
||||
mov ah, 0x0e
|
||||
mov al, 0x0a ; newline char
|
||||
int 0x10
|
||||
mov al, 0x0d ; carriage return
|
||||
int 0x10
|
||||
|
||||
popa
|
||||
ret
|
46
Part3/09_memory/boot/print_hex.asm
Normal file
46
Part3/09_memory/boot/print_hex.asm
Normal file
@ -0,0 +1,46 @@
|
||||
; receiving the data in 'dx'
|
||||
; For the examples we'll assume that we're called with dx=0x1234
|
||||
print_hex:
|
||||
pusha
|
||||
|
||||
mov cx, 0 ; our index variable
|
||||
|
||||
; Strategy: get the last char of 'dx', then convert to ASCII
|
||||
; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N.
|
||||
; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40
|
||||
; Then, move the ASCII byte to the correct position on the resulting string
|
||||
hex_loop:
|
||||
cmp cx, 4 ; loop 4 times
|
||||
je end
|
||||
|
||||
; 1. convert last char of 'dx' to ascii
|
||||
mov ax, dx ; we will use 'ax' as our working register
|
||||
and ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zeros
|
||||
add al, 0x30 ; add 0x30 to N to convert it to ASCII "N"
|
||||
cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F'
|
||||
jle step2
|
||||
add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7
|
||||
|
||||
step2:
|
||||
; 2. get the correct position of the string to place our ASCII char
|
||||
; bx <- base address + string length - index of char
|
||||
mov bx, HEX_OUT + 5 ; base + length
|
||||
sub bx, cx ; our index variable
|
||||
mov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx'
|
||||
ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234
|
||||
|
||||
; increment index and loop
|
||||
add cx, 1
|
||||
jmp hex_loop
|
||||
|
||||
end:
|
||||
; prepare the parameter and call the function
|
||||
; remember that print receives parameters in 'bx'
|
||||
mov bx, HEX_OUT
|
||||
call print
|
||||
|
||||
popa
|
||||
ret
|
||||
|
||||
HEX_OUT:
|
||||
db '0x0000',0 ; reserve memory for our new string
|
22
Part3/09_memory/boot/switch_pm.asm
Normal file
22
Part3/09_memory/boot/switch_pm.asm
Normal file
@ -0,0 +1,22 @@
|
||||
[bits 16]
|
||||
switch_to_pm:
|
||||
cli ; 1. disable interrupts
|
||||
lgdt [gdt_descriptor] ; 2. load the GDT descriptor
|
||||
mov eax, cr0
|
||||
or eax, 0x1 ; 3. set 32-bit mode bit in cr0
|
||||
mov cr0, eax
|
||||
jmp CODE_SEG:init_pm ; 4. far jump by using a different segment
|
||||
|
||||
[bits 32]
|
||||
init_pm: ; we are now using 32-bit instructions
|
||||
mov ax, DATA_SEG ; 5. update the segment registers
|
||||
mov ds, ax
|
||||
mov ss, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
mov ebp, 0x90000 ; 6. update the stack right at the top of the free space
|
||||
mov esp, ebp
|
||||
|
||||
call BEGIN_PM ; 7. Call a well-known label with useful code
|
16
Part3/09_memory/cpu/idt.c
Normal file
16
Part3/09_memory/cpu/idt.c
Normal file
@ -0,0 +1,16 @@
|
||||
#include "idt.h"
|
||||
|
||||
void set_idt_gate(int n, u32 handler) {
|
||||
idt[n].low_offset = low_16(handler);
|
||||
idt[n].sel = KERNEL_CS;
|
||||
idt[n].always0 = 0;
|
||||
idt[n].flags = 0x8E;
|
||||
idt[n].high_offset = high_16(handler);
|
||||
}
|
||||
|
||||
void set_idt() {
|
||||
idt_reg.base = (u32) &idt;
|
||||
idt_reg.limit = IDT_ENTRIES * sizeof(idt_gate_t) - 1;
|
||||
/* Don't make the mistake of loading &idt -- always load &idt_reg */
|
||||
__asm__ __volatile__("lidtl (%0)" : : "r" (&idt_reg));
|
||||
}
|
39
Part3/09_memory/cpu/idt.h
Normal file
39
Part3/09_memory/cpu/idt.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef IDT_H
|
||||
#define IDT_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/* Segment selectors */
|
||||
#define KERNEL_CS 0x08
|
||||
|
||||
/* How every interrupt gate (handler) is defined */
|
||||
typedef struct {
|
||||
u16 low_offset; /* Lower 16 bits of handler function address */
|
||||
u16 sel; /* Kernel segment selector */
|
||||
u8 always0;
|
||||
/* First byte
|
||||
* Bit 7: "Interrupt is present"
|
||||
* Bits 6-5: Privilege level of caller (0=kernel..3=user)
|
||||
* Bit 4: Set to 0 for interrupt gates
|
||||
* Bits 3-0: bits 1110 = decimal 14 = "32 bit interrupt gate" */
|
||||
u8 flags;
|
||||
u16 high_offset; /* Higher 16 bits of handler function address */
|
||||
} __attribute__((packed)) idt_gate_t ;
|
||||
|
||||
/* A pointer to the array of interrupt handlers.
|
||||
* Assembly instruction 'lidt' will read it */
|
||||
typedef struct {
|
||||
u16 limit;
|
||||
u32 base;
|
||||
} __attribute__((packed)) idt_register_t;
|
||||
|
||||
#define IDT_ENTRIES 256
|
||||
idt_gate_t idt[IDT_ENTRIES];
|
||||
idt_register_t idt_reg;
|
||||
|
||||
|
||||
/* Functions implemented in idt.c */
|
||||
void set_idt_gate(int n, u32 handler);
|
||||
void set_idt();
|
||||
|
||||
#endif
|
425
Part3/09_memory/cpu/interrupt.asm
Normal file
425
Part3/09_memory/cpu/interrupt.asm
Normal file
@ -0,0 +1,425 @@
|
||||
; Defined in isr.c
|
||||
[extern isr_handler]
|
||||
[extern irq_handler]
|
||||
|
||||
; Common ISR code
|
||||
isr_common_stub:
|
||||
; 1. Save CPU state
|
||||
pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax
|
||||
mov ax, ds ; Lower 16-bits of eax = ds.
|
||||
push eax ; save the data segment descriptor
|
||||
mov ax, 0x10 ; kernel data segment descriptor
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
; 2. Call C handler
|
||||
call isr_handler
|
||||
|
||||
; 3. Restore state
|
||||
pop eax
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
popa
|
||||
add esp, 8 ; Cleans up the pushed error code and pushed ISR number
|
||||
sti
|
||||
iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
|
||||
|
||||
; Common IRQ code. Identical to ISR code except for the 'call'
|
||||
; and the 'pop ebx'
|
||||
irq_common_stub:
|
||||
pusha
|
||||
mov ax, ds
|
||||
push eax
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
call irq_handler ; Different than the ISR code
|
||||
pop ebx ; Different than the ISR code
|
||||
mov ds, bx
|
||||
mov es, bx
|
||||
mov fs, bx
|
||||
mov gs, bx
|
||||
popa
|
||||
add esp, 8
|
||||
sti
|
||||
iret
|
||||
|
||||
; We don't get information about which interrupt was caller
|
||||
; when the handler is run, so we will need to have a different handler
|
||||
; for every interrupt.
|
||||
; Furthermore, some interrupts push an error code onto the stack but others
|
||||
; don't, so we will push a dummy error code for those which don't, so that
|
||||
; we have a consistent stack for all of them.
|
||||
|
||||
; First make the ISRs global
|
||||
global isr0
|
||||
global isr1
|
||||
global isr2
|
||||
global isr3
|
||||
global isr4
|
||||
global isr5
|
||||
global isr6
|
||||
global isr7
|
||||
global isr8
|
||||
global isr9
|
||||
global isr10
|
||||
global isr11
|
||||
global isr12
|
||||
global isr13
|
||||
global isr14
|
||||
global isr15
|
||||
global isr16
|
||||
global isr17
|
||||
global isr18
|
||||
global isr19
|
||||
global isr20
|
||||
global isr21
|
||||
global isr22
|
||||
global isr23
|
||||
global isr24
|
||||
global isr25
|
||||
global isr26
|
||||
global isr27
|
||||
global isr28
|
||||
global isr29
|
||||
global isr30
|
||||
global isr31
|
||||
; IRQs
|
||||
global irq0
|
||||
global irq1
|
||||
global irq2
|
||||
global irq3
|
||||
global irq4
|
||||
global irq5
|
||||
global irq6
|
||||
global irq7
|
||||
global irq8
|
||||
global irq9
|
||||
global irq10
|
||||
global irq11
|
||||
global irq12
|
||||
global irq13
|
||||
global irq14
|
||||
global irq15
|
||||
|
||||
; 0: Divide By Zero Exception
|
||||
isr0:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 0
|
||||
jmp isr_common_stub
|
||||
|
||||
; 1: Debug Exception
|
||||
isr1:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 1
|
||||
jmp isr_common_stub
|
||||
|
||||
; 2: Non Maskable Interrupt Exception
|
||||
isr2:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 2
|
||||
jmp isr_common_stub
|
||||
|
||||
; 3: Int 3 Exception
|
||||
isr3:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 3
|
||||
jmp isr_common_stub
|
||||
|
||||
; 4: INTO Exception
|
||||
isr4:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 4
|
||||
jmp isr_common_stub
|
||||
|
||||
; 5: Out of Bounds Exception
|
||||
isr5:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 5
|
||||
jmp isr_common_stub
|
||||
|
||||
; 6: Invalid Opcode Exception
|
||||
isr6:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 6
|
||||
jmp isr_common_stub
|
||||
|
||||
; 7: Coprocessor Not Available Exception
|
||||
isr7:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 7
|
||||
jmp isr_common_stub
|
||||
|
||||
; 8: Double Fault Exception (With Error Code!)
|
||||
isr8:
|
||||
cli
|
||||
push byte 8
|
||||
jmp isr_common_stub
|
||||
|
||||
; 9: Coprocessor Segment Overrun Exception
|
||||
isr9:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 9
|
||||
jmp isr_common_stub
|
||||
|
||||
; 10: Bad TSS Exception (With Error Code!)
|
||||
isr10:
|
||||
cli
|
||||
push byte 10
|
||||
jmp isr_common_stub
|
||||
|
||||
; 11: Segment Not Present Exception (With Error Code!)
|
||||
isr11:
|
||||
cli
|
||||
push byte 11
|
||||
jmp isr_common_stub
|
||||
|
||||
; 12: Stack Fault Exception (With Error Code!)
|
||||
isr12:
|
||||
cli
|
||||
push byte 12
|
||||
jmp isr_common_stub
|
||||
|
||||
; 13: General Protection Fault Exception (With Error Code!)
|
||||
isr13:
|
||||
cli
|
||||
push byte 13
|
||||
jmp isr_common_stub
|
||||
|
||||
; 14: Page Fault Exception (With Error Code!)
|
||||
isr14:
|
||||
cli
|
||||
push byte 14
|
||||
jmp isr_common_stub
|
||||
|
||||
; 15: Reserved Exception
|
||||
isr15:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 15
|
||||
jmp isr_common_stub
|
||||
|
||||
; 16: Floating Point Exception
|
||||
isr16:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 16
|
||||
jmp isr_common_stub
|
||||
|
||||
; 17: Alignment Check Exception
|
||||
isr17:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 17
|
||||
jmp isr_common_stub
|
||||
|
||||
; 18: Machine Check Exception
|
||||
isr18:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 18
|
||||
jmp isr_common_stub
|
||||
|
||||
; 19: Reserved
|
||||
isr19:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 19
|
||||
jmp isr_common_stub
|
||||
|
||||
; 20: Reserved
|
||||
isr20:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 20
|
||||
jmp isr_common_stub
|
||||
|
||||
; 21: Reserved
|
||||
isr21:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 21
|
||||
jmp isr_common_stub
|
||||
|
||||
; 22: Reserved
|
||||
isr22:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 22
|
||||
jmp isr_common_stub
|
||||
|
||||
; 23: Reserved
|
||||
isr23:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 23
|
||||
jmp isr_common_stub
|
||||
|
||||
; 24: Reserved
|
||||
isr24:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 24
|
||||
jmp isr_common_stub
|
||||
|
||||
; 25: Reserved
|
||||
isr25:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 25
|
||||
jmp isr_common_stub
|
||||
|
||||
; 26: Reserved
|
||||
isr26:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 26
|
||||
jmp isr_common_stub
|
||||
|
||||
; 27: Reserved
|
||||
isr27:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 27
|
||||
jmp isr_common_stub
|
||||
|
||||
; 28: Reserved
|
||||
isr28:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 28
|
||||
jmp isr_common_stub
|
||||
|
||||
; 29: Reserved
|
||||
isr29:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 29
|
||||
jmp isr_common_stub
|
||||
|
||||
; 30: Reserved
|
||||
isr30:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 30
|
||||
jmp isr_common_stub
|
||||
|
||||
; 31: Reserved
|
||||
isr31:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 31
|
||||
jmp isr_common_stub
|
||||
|
||||
; IRQ handlers
|
||||
irq0:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 32
|
||||
jmp irq_common_stub
|
||||
|
||||
irq1:
|
||||
cli
|
||||
push byte 1
|
||||
push byte 33
|
||||
jmp irq_common_stub
|
||||
|
||||
irq2:
|
||||
cli
|
||||
push byte 2
|
||||
push byte 34
|
||||
jmp irq_common_stub
|
||||
|
||||
irq3:
|
||||
cli
|
||||
push byte 3
|
||||
push byte 35
|
||||
jmp irq_common_stub
|
||||
|
||||
irq4:
|
||||
cli
|
||||
push byte 4
|
||||
push byte 36
|
||||
jmp irq_common_stub
|
||||
|
||||
irq5:
|
||||
cli
|
||||
push byte 5
|
||||
push byte 37
|
||||
jmp irq_common_stub
|
||||
|
||||
irq6:
|
||||
cli
|
||||
push byte 6
|
||||
push byte 38
|
||||
jmp irq_common_stub
|
||||
|
||||
irq7:
|
||||
cli
|
||||
push byte 7
|
||||
push byte 39
|
||||
jmp irq_common_stub
|
||||
|
||||
irq8:
|
||||
cli
|
||||
push byte 8
|
||||
push byte 40
|
||||
jmp irq_common_stub
|
||||
|
||||
irq9:
|
||||
cli
|
||||
push byte 9
|
||||
push byte 41
|
||||
jmp irq_common_stub
|
||||
|
||||
irq10:
|
||||
cli
|
||||
push byte 10
|
||||
push byte 42
|
||||
jmp irq_common_stub
|
||||
|
||||
irq11:
|
||||
cli
|
||||
push byte 11
|
||||
push byte 43
|
||||
jmp irq_common_stub
|
||||
|
||||
irq12:
|
||||
cli
|
||||
push byte 12
|
||||
push byte 44
|
||||
jmp irq_common_stub
|
||||
|
||||
irq13:
|
||||
cli
|
||||
push byte 13
|
||||
push byte 45
|
||||
jmp irq_common_stub
|
||||
|
||||
irq14:
|
||||
cli
|
||||
push byte 14
|
||||
push byte 46
|
||||
jmp irq_common_stub
|
||||
|
||||
irq15:
|
||||
cli
|
||||
push byte 15
|
||||
push byte 47
|
||||
jmp irq_common_stub
|
||||
|
153
Part3/09_memory/cpu/isr.c
Normal file
153
Part3/09_memory/cpu/isr.c
Normal file
@ -0,0 +1,153 @@
|
||||
#include "isr.h"
|
||||
#include "idt.h"
|
||||
#include "../drivers/screen.h"
|
||||
#include "../drivers/keyboard.h"
|
||||
#include "../libc/string.h"
|
||||
#include "timer.h"
|
||||
#include "ports.h"
|
||||
|
||||
isr_t interrupt_handlers[256];
|
||||
|
||||
/* Can't do this with a loop because we need the address
|
||||
* of the function names */
|
||||
void isr_install() {
|
||||
set_idt_gate(0, (u32)isr0);
|
||||
set_idt_gate(1, (u32)isr1);
|
||||
set_idt_gate(2, (u32)isr2);
|
||||
set_idt_gate(3, (u32)isr3);
|
||||
set_idt_gate(4, (u32)isr4);
|
||||
set_idt_gate(5, (u32)isr5);
|
||||
set_idt_gate(6, (u32)isr6);
|
||||
set_idt_gate(7, (u32)isr7);
|
||||
set_idt_gate(8, (u32)isr8);
|
||||
set_idt_gate(9, (u32)isr9);
|
||||
set_idt_gate(10, (u32)isr10);
|
||||
set_idt_gate(11, (u32)isr11);
|
||||
set_idt_gate(12, (u32)isr12);
|
||||
set_idt_gate(13, (u32)isr13);
|
||||
set_idt_gate(14, (u32)isr14);
|
||||
set_idt_gate(15, (u32)isr15);
|
||||
set_idt_gate(16, (u32)isr16);
|
||||
set_idt_gate(17, (u32)isr17);
|
||||
set_idt_gate(18, (u32)isr18);
|
||||
set_idt_gate(19, (u32)isr19);
|
||||
set_idt_gate(20, (u32)isr20);
|
||||
set_idt_gate(21, (u32)isr21);
|
||||
set_idt_gate(22, (u32)isr22);
|
||||
set_idt_gate(23, (u32)isr23);
|
||||
set_idt_gate(24, (u32)isr24);
|
||||
set_idt_gate(25, (u32)isr25);
|
||||
set_idt_gate(26, (u32)isr26);
|
||||
set_idt_gate(27, (u32)isr27);
|
||||
set_idt_gate(28, (u32)isr28);
|
||||
set_idt_gate(29, (u32)isr29);
|
||||
set_idt_gate(30, (u32)isr30);
|
||||
set_idt_gate(31, (u32)isr31);
|
||||
|
||||
// Remap the PIC
|
||||
port_byte_out(0x20, 0x11);
|
||||
port_byte_out(0xA0, 0x11);
|
||||
port_byte_out(0x21, 0x20);
|
||||
port_byte_out(0xA1, 0x28);
|
||||
port_byte_out(0x21, 0x04);
|
||||
port_byte_out(0xA1, 0x02);
|
||||
port_byte_out(0x21, 0x01);
|
||||
port_byte_out(0xA1, 0x01);
|
||||
port_byte_out(0x21, 0x0);
|
||||
port_byte_out(0xA1, 0x0);
|
||||
|
||||
// Install the IRQs
|
||||
set_idt_gate(32, (u32)irq0);
|
||||
set_idt_gate(33, (u32)irq1);
|
||||
set_idt_gate(34, (u32)irq2);
|
||||
set_idt_gate(35, (u32)irq3);
|
||||
set_idt_gate(36, (u32)irq4);
|
||||
set_idt_gate(37, (u32)irq5);
|
||||
set_idt_gate(38, (u32)irq6);
|
||||
set_idt_gate(39, (u32)irq7);
|
||||
set_idt_gate(40, (u32)irq8);
|
||||
set_idt_gate(41, (u32)irq9);
|
||||
set_idt_gate(42, (u32)irq10);
|
||||
set_idt_gate(43, (u32)irq11);
|
||||
set_idt_gate(44, (u32)irq12);
|
||||
set_idt_gate(45, (u32)irq13);
|
||||
set_idt_gate(46, (u32)irq14);
|
||||
set_idt_gate(47, (u32)irq15);
|
||||
|
||||
set_idt(); // Load with ASM
|
||||
}
|
||||
|
||||
/* To print the message which defines every exception */
|
||||
char *exception_messages[] = {
|
||||
"Division By Zero",
|
||||
"Debug",
|
||||
"Non Maskable Interrupt",
|
||||
"Breakpoint",
|
||||
"Into Detected Overflow",
|
||||
"Out of Bounds",
|
||||
"Invalid Opcode",
|
||||
"No Coprocessor",
|
||||
|
||||
"Double Fault",
|
||||
"Coprocessor Segment Overrun",
|
||||
"Bad TSS",
|
||||
"Segment Not Present",
|
||||
"Stack Fault",
|
||||
"General Protection Fault",
|
||||
"Page Fault",
|
||||
"Unknown Interrupt",
|
||||
|
||||
"Coprocessor Fault",
|
||||
"Alignment Check",
|
||||
"Machine Check",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved"
|
||||
};
|
||||
|
||||
void isr_handler(registers_t r) {
|
||||
kprint("received interrupt: ");
|
||||
char s[3];
|
||||
int_to_ascii(r.int_no, s, 3);
|
||||
kprint(s);
|
||||
kprint("\n");
|
||||
kprint(exception_messages[r.int_no]);
|
||||
kprint("\n");
|
||||
}
|
||||
|
||||
void register_interrupt_handler(u8 n, isr_t handler) {
|
||||
interrupt_handlers[n] = handler;
|
||||
}
|
||||
|
||||
void irq_handler(registers_t r) {
|
||||
/* After every interrupt we need to send an EOI to the PICs
|
||||
* or they will not send another interrupt again */
|
||||
if (r.int_no >= 40) port_byte_out(0xA0, 0x20); /* slave */
|
||||
port_byte_out(0x20, 0x20); /* master */
|
||||
|
||||
/* Handle the interrupt in a more modular way */
|
||||
if (interrupt_handlers[r.int_no] != 0) {
|
||||
isr_t handler = interrupt_handlers[r.int_no];
|
||||
handler(r);
|
||||
}
|
||||
}
|
||||
|
||||
void irq_install() {
|
||||
/* Enable interruptions */
|
||||
asm volatile("sti");
|
||||
/* IRQ0: timer */
|
||||
init_timer(50);
|
||||
/* IRQ1: keyboard */
|
||||
init_keyboard();
|
||||
}
|
89
Part3/09_memory/cpu/isr.h
Normal file
89
Part3/09_memory/cpu/isr.h
Normal file
@ -0,0 +1,89 @@
|
||||
#ifndef ISR_H
|
||||
#define ISR_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/* ISRs reserved for CPU exceptions */
|
||||
extern void isr0();
|
||||
extern void isr1();
|
||||
extern void isr2();
|
||||
extern void isr3();
|
||||
extern void isr4();
|
||||
extern void isr5();
|
||||
extern void isr6();
|
||||
extern void isr7();
|
||||
extern void isr8();
|
||||
extern void isr9();
|
||||
extern void isr10();
|
||||
extern void isr11();
|
||||
extern void isr12();
|
||||
extern void isr13();
|
||||
extern void isr14();
|
||||
extern void isr15();
|
||||
extern void isr16();
|
||||
extern void isr17();
|
||||
extern void isr18();
|
||||
extern void isr19();
|
||||
extern void isr20();
|
||||
extern void isr21();
|
||||
extern void isr22();
|
||||
extern void isr23();
|
||||
extern void isr24();
|
||||
extern void isr25();
|
||||
extern void isr26();
|
||||
extern void isr27();
|
||||
extern void isr28();
|
||||
extern void isr29();
|
||||
extern void isr30();
|
||||
extern void isr31();
|
||||
/* IRQ definitions */
|
||||
extern void irq0();
|
||||
extern void irq1();
|
||||
extern void irq2();
|
||||
extern void irq3();
|
||||
extern void irq4();
|
||||
extern void irq5();
|
||||
extern void irq6();
|
||||
extern void irq7();
|
||||
extern void irq8();
|
||||
extern void irq9();
|
||||
extern void irq10();
|
||||
extern void irq11();
|
||||
extern void irq12();
|
||||
extern void irq13();
|
||||
extern void irq14();
|
||||
extern void irq15();
|
||||
|
||||
#define IRQ0 32
|
||||
#define IRQ1 33
|
||||
#define IRQ2 34
|
||||
#define IRQ3 35
|
||||
#define IRQ4 36
|
||||
#define IRQ5 37
|
||||
#define IRQ6 38
|
||||
#define IRQ7 39
|
||||
#define IRQ8 40
|
||||
#define IRQ9 41
|
||||
#define IRQ10 42
|
||||
#define IRQ11 43
|
||||
#define IRQ12 44
|
||||
#define IRQ13 45
|
||||
#define IRQ14 46
|
||||
#define IRQ15 47
|
||||
|
||||
/* Struct which aggregates many registers */
|
||||
typedef struct {
|
||||
u32 ds; /* Data segment selector */
|
||||
u32 edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */
|
||||
u32 int_no, err_code; /* Interrupt number and error code (if applicable) */
|
||||
u32 eip, cs, eflags, useresp, ss; /* Pushed by the processor automatically */
|
||||
} registers_t;
|
||||
|
||||
void isr_install();
|
||||
void isr_handler(registers_t r);
|
||||
void irq_install();
|
||||
|
||||
typedef void (*isr_t)(registers_t);
|
||||
void register_interrupt_handler(u8 n, isr_t handler);
|
||||
|
||||
#endif
|
37
Part3/09_memory/cpu/ports.c
Normal file
37
Part3/09_memory/cpu/ports.c
Normal file
@ -0,0 +1,37 @@
|
||||
#include "ports.h"
|
||||
|
||||
/**
|
||||
* Read a byte from the specified port
|
||||
*/
|
||||
u8 port_byte_in (u16 port) {
|
||||
u8 result;
|
||||
/* Inline assembler syntax
|
||||
* !! Notice how the source and destination registers are switched from NASM !!
|
||||
*
|
||||
* '"=a" (result)'; set '=' the C variable '(result)' to the value of register e'a'x
|
||||
* '"d" (port)': map the C variable '(port)' into e'd'x register
|
||||
*
|
||||
* Inputs and outputs are separated by colons
|
||||
*/
|
||||
__asm__("in %%dx, %%al" : "=a" (result) : "d" (port));
|
||||
return result;
|
||||
}
|
||||
|
||||
void port_byte_out (u16 port, u8 data) {
|
||||
/* Notice how here both registers are mapped to C variables and
|
||||
* nothing is returned, thus, no equals '=' in the asm syntax
|
||||
* However we see a comma since there are two variables in the input area
|
||||
* and none in the 'return' area
|
||||
*/
|
||||
__asm__ __volatile__("out %%al, %%dx" : : "a" (data), "d" (port));
|
||||
}
|
||||
|
||||
u16 port_word_in (u16 port) {
|
||||
u16 result;
|
||||
__asm__("in %%dx, %%ax" : "=a" (result) : "d" (port));
|
||||
return result;
|
||||
}
|
||||
|
||||
void port_word_out (u16 port, u16 data) {
|
||||
__asm__ __volatile__("out %%ax, %%dx" : : "a" (data), "d" (port));
|
||||
}
|
11
Part3/09_memory/cpu/ports.h
Normal file
11
Part3/09_memory/cpu/ports.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef PORTS_H
|
||||
#define PORTS_H
|
||||
|
||||
#include "../cpu/types.h"
|
||||
|
||||
unsigned char port_byte_in (u16 port);
|
||||
void port_byte_out (u16 port, u8 data);
|
||||
unsigned short port_word_in (u16 port);
|
||||
void port_word_out (u16 port, u16 data);
|
||||
|
||||
#endif
|
25
Part3/09_memory/cpu/timer.c
Normal file
25
Part3/09_memory/cpu/timer.c
Normal file
@ -0,0 +1,25 @@
|
||||
#include "timer.h"
|
||||
#include "isr.h"
|
||||
#include "ports.h"
|
||||
#include "../libc/function.h"
|
||||
|
||||
u32 tick = 0;
|
||||
|
||||
static void timer_callback(registers_t regs) {
|
||||
tick++;
|
||||
UNUSED(regs);
|
||||
}
|
||||
|
||||
void init_timer(u32 freq) {
|
||||
/* Install the function we just wrote */
|
||||
register_interrupt_handler(IRQ0, timer_callback);
|
||||
|
||||
/* Get the PIT value: hardware clock at 1193180 Hz */
|
||||
u32 divisor = 1193180 / freq;
|
||||
u8 low = (u8)(divisor & 0xFF);
|
||||
u8 high = (u8)( (divisor >> 8) & 0xFF);
|
||||
/* Send the command */
|
||||
port_byte_out(0x43, 0x36); /* Command port */
|
||||
port_byte_out(0x40, low);
|
||||
port_byte_out(0x40, high);
|
||||
}
|
8
Part3/09_memory/cpu/timer.h
Normal file
8
Part3/09_memory/cpu/timer.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef TIMER_H
|
||||
#define TIMER_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
void init_timer(u32 freq);
|
||||
|
||||
#endif
|
35
Part3/09_memory/cpu/types.h
Normal file
35
Part3/09_memory/cpu/types.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef TYPES_H
|
||||
#define TYPES_H
|
||||
|
||||
/* Instead of using 'chars' to allocate non-character bytes,
|
||||
* we will use these new type with no semantic meaning */
|
||||
typedef unsigned int u32;
|
||||
typedef int s32;
|
||||
typedef unsigned short u16;
|
||||
typedef short s16;
|
||||
typedef unsigned char u8;
|
||||
typedef char s8;
|
||||
|
||||
#define bool u32
|
||||
#define true 1
|
||||
#define TRUE 1
|
||||
#define false 0
|
||||
#define FALSE 1
|
||||
|
||||
#define NULL ((void *) 0)
|
||||
#define low_16(address) (u16)((address) & 0xFFFF)
|
||||
#define high_16(address) (u16)(((address) >> 16) & 0xFFFF)
|
||||
|
||||
typedef struct _node {
|
||||
u32 id; // generic unique id
|
||||
u32 base_register; // the memory address start of allocation
|
||||
u32 limit_register; // the size in bytes of the allocation
|
||||
bool ft_hole_mem; // FALSE = hole, TRUE = memory allocation
|
||||
|
||||
struct _node *next;
|
||||
struct _node *previous;
|
||||
// current size 6 words = 24bytes
|
||||
} node;
|
||||
#define NODE_SIZE 24
|
||||
|
||||
#endif
|
51
Part3/09_memory/drivers/keyboard.c
Normal file
51
Part3/09_memory/drivers/keyboard.c
Normal file
@ -0,0 +1,51 @@
|
||||
#include "keyboard.h"
|
||||
#include "../cpu/ports.h"
|
||||
#include "../cpu/isr.h"
|
||||
#include "screen.h"
|
||||
#include "../libc/string.h"
|
||||
#include "../libc/function.h"
|
||||
#include "../kernel/kernel.h"
|
||||
|
||||
#define BACKSPACE 0x0E
|
||||
#define ENTER 0x1C
|
||||
|
||||
static char key_buffer[256];
|
||||
|
||||
#define SC_MAX 57
|
||||
const char *sc_name[] = { "ERROR", "Esc", "1", "2", "3", "4", "5", "6",
|
||||
"7", "8", "9", "0", "-", "=", "Backspace", "Tab", "Q", "W", "E",
|
||||
"R", "T", "Y", "U", "I", "O", "P", "[", "]", "Enter", "Lctrl",
|
||||
"A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "`",
|
||||
"LShift", "\\", "Z", "X", "C", "V", "B", "N", "M", ",", ".",
|
||||
"/", "RShift", "Keypad *", "LAlt", "Spacebar"};
|
||||
const char sc_ascii[] = { '?', '?', '1', '2', '3', '4', '5', '6',
|
||||
'7', '8', '9', '0', '-', '=', '?', '?', 'Q', 'W', 'E', 'R', 'T', 'Y',
|
||||
'U', 'I', 'O', 'P', '[', ']', '?', '?', 'A', 'S', 'D', 'F', 'G',
|
||||
'H', 'J', 'K', 'L', ';', '\'', '`', '?', '\\', 'Z', 'X', 'C', 'V',
|
||||
'B', 'N', 'M', ',', '.', '/', '?', '?', '?', ' '};
|
||||
|
||||
static void keyboard_callback(registers_t regs) {
|
||||
/* The PIC leaves us the scancode in port 0x60 */
|
||||
u8 scancode = port_byte_in(0x60);
|
||||
|
||||
if (scancode > SC_MAX) return;
|
||||
if (scancode == BACKSPACE) {
|
||||
backspace(key_buffer);
|
||||
kprint_backspace();
|
||||
} else if (scancode == ENTER) {
|
||||
kprint("\n");
|
||||
user_input(key_buffer); /* kernel-controlled function */
|
||||
key_buffer[0] = '\0';
|
||||
} else {
|
||||
char letter = sc_ascii[(int)scancode];
|
||||
/* Remember that kprint only accepts char[] */
|
||||
char str[2] = {letter, '\0'};
|
||||
append(key_buffer, letter);
|
||||
kprint(str);
|
||||
}
|
||||
UNUSED(regs);
|
||||
}
|
||||
|
||||
void init_keyboard() {
|
||||
register_interrupt_handler(IRQ1, keyboard_callback);
|
||||
}
|
3
Part3/09_memory/drivers/keyboard.h
Normal file
3
Part3/09_memory/drivers/keyboard.h
Normal file
@ -0,0 +1,3 @@
|
||||
#include "../cpu/types.h"
|
||||
|
||||
void init_keyboard();
|
161
Part3/09_memory/drivers/screen.c
Normal file
161
Part3/09_memory/drivers/screen.c
Normal file
@ -0,0 +1,161 @@
|
||||
#include "screen.h"
|
||||
#include "../cpu/ports.h"
|
||||
#include "../libc/mem.h"
|
||||
|
||||
/* Declaration of private functions */
|
||||
int get_cursor_offset();
|
||||
void set_cursor_offset(int offset);
|
||||
int print_char(char c, int col, int row, char attr);
|
||||
int get_offset(int col, int row);
|
||||
int get_offset_row(int offset);
|
||||
int get_offset_col(int offset);
|
||||
|
||||
/**********************************************************
|
||||
* Public Kernel API functions *
|
||||
**********************************************************/
|
||||
|
||||
/**
|
||||
* Print a message on the specified location
|
||||
* If col, row, are negative, we will use the current offset
|
||||
*/
|
||||
void kprint_at(char *message, int col, int row) {
|
||||
/* Set cursor if col/row are negative */
|
||||
int offset;
|
||||
if (col >= 0 && row >= 0)
|
||||
offset = get_offset(col, row);
|
||||
else {
|
||||
offset = get_cursor_offset();
|
||||
row = get_offset_row(offset);
|
||||
col = get_offset_col(offset);
|
||||
}
|
||||
|
||||
/* Loop through message and print it */
|
||||
int i = 0;
|
||||
while (message[i] != 0) {
|
||||
offset = print_char(message[i++], col, row, WHITE_ON_BLACK);
|
||||
/* Compute row/col for next iteration */
|
||||
row = get_offset_row(offset);
|
||||
col = get_offset_col(offset);
|
||||
}
|
||||
}
|
||||
|
||||
void kprint(char *message) {
|
||||
kprint_at(message, -1, -1);
|
||||
}
|
||||
|
||||
void kprint_hex( char *message, u32 hex, u32 digits)
|
||||
{
|
||||
if( digits <= 0) {
|
||||
return;
|
||||
}
|
||||
char hex_str[digits+1];
|
||||
hex_to_ascii( hex, hex_str, digits);
|
||||
kprint( message);
|
||||
kprint( hex_str);
|
||||
return;
|
||||
}
|
||||
|
||||
void kprint_backspace() {
|
||||
int offset = get_cursor_offset()-2;
|
||||
int row = get_offset_row(offset);
|
||||
int col = get_offset_col(offset);
|
||||
print_char(0x08, col, row, WHITE_ON_BLACK);
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************
|
||||
* Private kernel functions *
|
||||
**********************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* Innermost print function for our kernel, directly accesses the video memory
|
||||
*
|
||||
* If 'col' and 'row' are negative, we will print at current cursor location
|
||||
* If 'attr' is zero it will use 'white on black' as default
|
||||
* Returns the offset of the next character
|
||||
* Sets the video cursor to the returned offset
|
||||
*/
|
||||
int print_char(char c, int col, int row, char attr) {
|
||||
u8 *vidmem = (u8*) VIDEO_ADDRESS;
|
||||
if (!attr) attr = WHITE_ON_BLACK;
|
||||
|
||||
/* Error control: print a red 'E' if the coords aren't right */
|
||||
if (col >= MAX_COLS || row >= MAX_ROWS) {
|
||||
vidmem[2*(MAX_COLS)*(MAX_ROWS)-2] = 'E';
|
||||
vidmem[2*(MAX_COLS)*(MAX_ROWS)-1] = RED_ON_WHITE;
|
||||
return get_offset(col, row);
|
||||
}
|
||||
|
||||
int offset;
|
||||
if (col >= 0 && row >= 0) offset = get_offset(col, row);
|
||||
else offset = get_cursor_offset();
|
||||
|
||||
if (c == '\n') {
|
||||
row = get_offset_row(offset);
|
||||
offset = get_offset(0, row+1);
|
||||
} else if (c == 0x08) { /* Backspace */
|
||||
vidmem[offset] = ' ';
|
||||
vidmem[offset+1] = attr;
|
||||
} else {
|
||||
vidmem[offset] = c;
|
||||
vidmem[offset+1] = attr;
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
/* Check if the offset is over screen size and scroll */
|
||||
if (offset >= MAX_ROWS * MAX_COLS * 2) {
|
||||
int i;
|
||||
for (i = 1; i < MAX_ROWS; i++)
|
||||
memory_copy((u8*)(get_offset(0, i) + VIDEO_ADDRESS),
|
||||
(u8*)(get_offset(0, i-1) + VIDEO_ADDRESS),
|
||||
MAX_COLS * 2);
|
||||
|
||||
/* Blank last line */
|
||||
char *last_line = (char*) (get_offset(0, MAX_ROWS-1) + (u8*) VIDEO_ADDRESS);
|
||||
for (i = 0; i < MAX_COLS * 2; i++) last_line[i] = 0;
|
||||
|
||||
offset -= 2 * MAX_COLS;
|
||||
}
|
||||
|
||||
set_cursor_offset(offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
int get_cursor_offset() {
|
||||
/* Use the VGA ports to get the current cursor position
|
||||
* 1. Ask for high byte of the cursor offset (data 14)
|
||||
* 2. Ask for low byte (data 15)
|
||||
*/
|
||||
port_byte_out(REG_SCREEN_CTRL, 14);
|
||||
int offset = port_byte_in(REG_SCREEN_DATA) << 8; /* High byte: << 8 */
|
||||
port_byte_out(REG_SCREEN_CTRL, 15);
|
||||
offset += port_byte_in(REG_SCREEN_DATA);
|
||||
return offset * 2; /* Position * size of character cell */
|
||||
}
|
||||
|
||||
void set_cursor_offset(int offset) {
|
||||
/* Similar to get_cursor_offset, but instead of reading we write data */
|
||||
offset /= 2;
|
||||
port_byte_out(REG_SCREEN_CTRL, 14);
|
||||
port_byte_out(REG_SCREEN_DATA, (u8)(offset >> 8));
|
||||
port_byte_out(REG_SCREEN_CTRL, 15);
|
||||
port_byte_out(REG_SCREEN_DATA, (u8)(offset & 0xff));
|
||||
}
|
||||
|
||||
void clear_screen() {
|
||||
int screen_size = MAX_COLS * MAX_ROWS;
|
||||
int i;
|
||||
u8 *screen = (u8*) VIDEO_ADDRESS;
|
||||
|
||||
for (i = 0; i < screen_size; i++) {
|
||||
screen[i*2] = ' ';
|
||||
screen[i*2+1] = WHITE_ON_BLACK;
|
||||
}
|
||||
set_cursor_offset(get_offset(0, 0));
|
||||
}
|
||||
|
||||
|
||||
int get_offset(int col, int row) { return 2 * (row * MAX_COLS + col); }
|
||||
int get_offset_row(int offset) { return offset / (2 * MAX_COLS); }
|
||||
int get_offset_col(int offset) { return (offset - (get_offset_row(offset)*2*MAX_COLS))/2; }
|
23
Part3/09_memory/drivers/screen.h
Normal file
23
Part3/09_memory/drivers/screen.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef SCREEN_H
|
||||
#define SCREEN_H
|
||||
|
||||
#include "../cpu/types.h"
|
||||
|
||||
#define VIDEO_ADDRESS 0xb8000
|
||||
#define MAX_ROWS 25
|
||||
#define MAX_COLS 80
|
||||
#define WHITE_ON_BLACK 0x0f
|
||||
#define RED_ON_WHITE 0xf4
|
||||
|
||||
/* Screen i/o ports */
|
||||
#define REG_SCREEN_CTRL 0x3d4
|
||||
#define REG_SCREEN_DATA 0x3d5
|
||||
|
||||
/* Public kernel API */
|
||||
void clear_screen();
|
||||
void kprint_at(char *message, int col, int row);
|
||||
void kprint(char *message);
|
||||
void kprint_backspace();
|
||||
void kprint_hex( char *message, u32 hex, u32 digits);
|
||||
|
||||
#endif
|
102
Part3/09_memory/kernel/kernel.c
Normal file
102
Part3/09_memory/kernel/kernel.c
Normal file
@ -0,0 +1,102 @@
|
||||
#include "kernel.h"
|
||||
|
||||
// This only runs once and from there on forth only interrupts will cause something to happen
|
||||
// e.g. displaying to screen and taking input from keyboard
|
||||
void _start() {
|
||||
// Initialize Global Variables and set up interrrupt handlers
|
||||
// These variables are a vulnerability to OS security if someone can adjust
|
||||
// the lower value in the kernel code (exploiting, for example, an Intel address hack)
|
||||
// than kernel memory and even kernel code can be overwritten
|
||||
// https://www.wired.com/story/intel-lab-istare-hack-chips/
|
||||
// See libc/globals.h for KMEM_START and KMEM_END values
|
||||
// See libc/globals.h for UMEM_START and UMEM_END values
|
||||
kmem_addr = KMEM_START;
|
||||
kernel_mem_limit = KMEM_END;
|
||||
|
||||
umem_addr = UMEM_START;
|
||||
user_mem_limit = UMEM_END;
|
||||
|
||||
global_head = NULL;
|
||||
global_id = 0;
|
||||
|
||||
|
||||
isr_install();
|
||||
irq_install();
|
||||
|
||||
kprint("Type something, it will go through the kernel\n"
|
||||
"Type HELP to list commands\n> ");
|
||||
}
|
||||
|
||||
// An interrupt calls this function to parse what was typed in, this is, essentially, your
|
||||
// Command Line Interpreter for now.
|
||||
void user_input(char *input) {
|
||||
static u32 delete_id = 0; // static persistent variable for incrementing during test
|
||||
static node* umem_head = NULL; // static persistent head variable for contiguous block allocations
|
||||
if (strcmp(input, "END") == 0) {
|
||||
kprint("Stopping the CPU. Bye!\n");
|
||||
asm volatile("hlt");
|
||||
} else if (strcmp(input, "ADD") == 0) {
|
||||
umem_head = add_node( umem_head, 0x10000, 0x100, true, global_id++);
|
||||
} else if (strcmp(input, "LIST") == 0) {
|
||||
kprint("***** FORWARD ****\n");
|
||||
print_list( umem_head, true);
|
||||
kprint("***** REVERSE ****\n");
|
||||
print_list( umem_head, false);
|
||||
} else if (strcmp(input, "SHORTLIST") == 0) {
|
||||
shortprint_list( umem_head, true);
|
||||
kprint("\n******************\n");
|
||||
shortprint_list( umem_head, false);
|
||||
} else if (strcmp(input, "PAGE") == 0) {
|
||||
u32 phys_addr = 0;
|
||||
u32 page = umalloc(0x4200, 0, &phys_addr);
|
||||
kprint_hex( "Page: ", page, 10);
|
||||
kprint_hex(", physical address: ", phys_addr, 10);
|
||||
kprint("\n");
|
||||
} else if (strcmp(input, "DELETE") == 0) {
|
||||
umem_head = remove_node_by_id( umem_head, delete_id++);
|
||||
} else if (strcmp(input, "INSERT") == 0) {
|
||||
node *new_node = create_node( 0x15000, 0x1100, true, global_id++);
|
||||
node *insert_point = find_id( umem_head, 3);
|
||||
umem_head = insert_node( umem_head, insert_point, new_node, true);
|
||||
new_node = create_node( 0x18000, 0x2100, true, global_id++);
|
||||
insert_point = find_id( umem_head, 5);
|
||||
umem_head = insert_node( umem_head, insert_point, new_node, false);
|
||||
} else if (strcmp(input, "SORTA") == 0) {
|
||||
umem_head = hacksort_list( umem_head, true);
|
||||
} else if (strcmp(input, "SORTD") == 0) {
|
||||
umem_head = hacksort_list( umem_head, false);
|
||||
} else if (strcmp(input, "SWAP") == 0) {
|
||||
node *n1 = find_id( umem_head, 1);
|
||||
node *n2 = find_id( umem_head, 5);
|
||||
node *n3 = find_id( umem_head, 3);
|
||||
node *n4 = find_id( umem_head, 7);
|
||||
swap_node_data( n1, n2);
|
||||
swap_node_data( n2, n3);
|
||||
swap_node_data( n3, n4);
|
||||
} else if (strcmp(input, "TEST") == 0) {
|
||||
char s1[10] = "ABCDFFGH\0";
|
||||
char s2[10] = "ABCDEGH\0";
|
||||
int x = strncmp( s1, s2, 5);
|
||||
kprint_hex( "STRNCMP: ", x, 16);
|
||||
kprint("\n");
|
||||
|
||||
x = sstrlen( s2, 10);
|
||||
kprint_hex( "SSTRLEN: ", x, 10);
|
||||
kprint("\n");
|
||||
|
||||
x = strlen( s2);
|
||||
kprint_hex( "STRLEN: ", x, 10);
|
||||
kprint("\n");
|
||||
} else if (strcmp(input, "HELP") == 0) {
|
||||
kprint("Current Commands: ADD, LIST, SHORTLIST, PAGE, DELETE,\n");
|
||||
kprint(" : END, INSERT, SORTA, SORTD, SWAP, TEST, HELP\n");
|
||||
kprint(" Review the kernel.c source code to see what each command does.\n");
|
||||
kprint(" These are hard coded and are just examples, modify as you see fit.\n");
|
||||
kprint(" for example - TEST was just added so that I could test the strlen commands.\n");
|
||||
} else {
|
||||
kprint("You said: ");
|
||||
kprint(input);
|
||||
kprint("\n");
|
||||
}
|
||||
kprint("> ");
|
||||
}
|
13
Part3/09_memory/kernel/kernel.h
Normal file
13
Part3/09_memory/kernel/kernel.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef KERNEL_H
|
||||
#define KERNEL_H
|
||||
|
||||
#include "../cpu/isr.h"
|
||||
#include "../drivers/screen.h"
|
||||
#include "../libc/string.h"
|
||||
#include "../libc/globals.h"
|
||||
#include "../libc/mem.h"
|
||||
#include "../libc/linked.h"
|
||||
|
||||
void user_input(char *input);
|
||||
|
||||
#endif
|
8
Part3/09_memory/libc/function.h
Normal file
8
Part3/09_memory/libc/function.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef FUNCTION_H
|
||||
#define FUNCTION_H
|
||||
|
||||
/* Sometimes we want to keep parameters to a function for later use
|
||||
* and this is a solution to avoid the 'unused parameter' compiler warning */
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
#endif
|
11
Part3/09_memory/libc/globals.c
Normal file
11
Part3/09_memory/libc/globals.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include "globals.h"
|
||||
|
||||
u32 kmem_addr;
|
||||
u32 kernel_mem_limit;
|
||||
|
||||
u32 umem_addr;
|
||||
u32 user_mem_limit;
|
||||
|
||||
u32 global_id;
|
||||
|
||||
node *global_head;
|
23
Part3/09_memory/libc/globals.h
Normal file
23
Part3/09_memory/libc/globals.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef GLOBAL_H
|
||||
#define GLOBAL_H
|
||||
|
||||
#include "../cpu/types.h"
|
||||
#include "linked.h"
|
||||
|
||||
// Using defines allows this file to control the ranges here rather than updating the kernel code lines
|
||||
// somewhere else at various locations throughout the code
|
||||
// using ALL CAPS is a conventions for defines and constants, that varies from group to group
|
||||
#define KMEM_START 0x9000 // starts equal to this address for allocation
|
||||
#define KMEM_END 0x10000 // cannot be equal to or more than this address
|
||||
extern u32 kmem_addr; // The start value of allocation kernel datastructure allocations
|
||||
extern u32 kernel_mem_limit; // the upper limit of memory allowed for kernel memory allocations
|
||||
|
||||
#define UMEM_START 0x10000 // kernel memory for linked list nodes starts here
|
||||
#define UMEM_END 0x40000 // kernel memory for linked list nodes must end before here
|
||||
extern u32 umem_addr; // start of linked list memory allocation address
|
||||
extern u32 user_mem_limit; // end of linked list memory allocation address
|
||||
|
||||
extern u32 global_id; // Just an id for now but will eventually become PID
|
||||
|
||||
extern node *global_head; // Kernel data structure for storing memory allocation linked list
|
||||
#endif // GLOBAL_H
|
480
Part3/09_memory/libc/linked.c
Normal file
480
Part3/09_memory/libc/linked.c
Normal file
@ -0,0 +1,480 @@
|
||||
// linked.c ANSI C kernel environment linked list
|
||||
// requires kmalloc to allocate space for linked list
|
||||
|
||||
#include "linked.h"
|
||||
|
||||
// Create a blank node using kmalloc, then use allocated memory to store
|
||||
// a linked list node with the values passed in the parameters
|
||||
// You must capture the node returned with an lval result variable
|
||||
node *create_node( u32 base_register, u32 limit_register, bool ft_hole_mem, u32 id)
|
||||
{
|
||||
node *new_node = NULL;
|
||||
|
||||
new_node = (node *) kmalloc( NODE_SIZE, 0, (u32 *) new_node);
|
||||
new_node->id = id;
|
||||
new_node->base_register = base_register;
|
||||
new_node->limit_register = limit_register * (new_node->id + 1);
|
||||
new_node->ft_hole_mem = ft_hole_mem;
|
||||
new_node->next = NULL;
|
||||
new_node->previous = NULL;
|
||||
|
||||
return( new_node);
|
||||
}
|
||||
|
||||
// Creates a node from parameters using the above and then passes it to insert the node into the linked list
|
||||
// changes to *head will only affect a local stack variable
|
||||
// You must capture the new head returned with an lval result variable to update head changes
|
||||
// ft_hole_mem is not used currently but is included to maintain a "holes" list
|
||||
// true means update memory list, false means update holes list (if you were to use it)
|
||||
node *add_node( node *head, u32 base_register, u32 limit_register, bool ft_hole_mem, u32 id)
|
||||
{
|
||||
node *new_node = NULL;
|
||||
|
||||
new_node = create_node( base_register, limit_register, ft_hole_mem, id);
|
||||
head = _add_node( head, new_node);
|
||||
return( head);
|
||||
}
|
||||
|
||||
// place an already created node at the end of the list
|
||||
// changes to *head will only affect a local stack variable
|
||||
// You must capture the new head returned with an lval result variable to update head changes
|
||||
node *_add_node( node *head, node *new_node)
|
||||
{
|
||||
node *iterator = NULL;
|
||||
node *previous = NULL;
|
||||
|
||||
if( new_node == NULL) {
|
||||
return(head);
|
||||
}
|
||||
if( head == NULL) {
|
||||
head = new_node;
|
||||
return ( head );
|
||||
} // Stop here is head is empty and just set to new and return new head.
|
||||
|
||||
iterator = head->next; // Head is not NULL so continue with next node as iterator
|
||||
while( iterator != NULL) {
|
||||
previous = iterator;
|
||||
iterator = iterator->next;
|
||||
} // until iterator is null keep pointing new node back to previous node and iterator to next node
|
||||
|
||||
if( previous == NULL) { // just head existed, append new node ...
|
||||
new_node->previous = head;
|
||||
head->next = new_node;
|
||||
} else { // iterated to end of list, append new node ...
|
||||
new_node->previous = previous;
|
||||
previous->next = new_node;
|
||||
} // ... and return
|
||||
return( head);
|
||||
}
|
||||
|
||||
// Iterate to last node and list and then append new node to it
|
||||
node *append_node( node *head, node* new_node)
|
||||
{
|
||||
node *iterator = head;
|
||||
|
||||
if( new_node == NULL) {
|
||||
return( head);
|
||||
}
|
||||
if( head == NULL) {
|
||||
return( new_node);
|
||||
}
|
||||
while( iterator->next != NULL) { // step until at last node
|
||||
iterator = iterator->next;
|
||||
}
|
||||
iterator->next = new_node;
|
||||
new_node->previous = iterator;
|
||||
new_node->next = NULL;
|
||||
return(head);
|
||||
}
|
||||
|
||||
// Given an input id search list until id found
|
||||
// return lval of that *node value otherwise return a NULL value
|
||||
node *find_id( node *head, u32 id)
|
||||
{
|
||||
if( head == NULL) {
|
||||
return( head);
|
||||
}
|
||||
node *iterator = head;
|
||||
|
||||
while(( iterator != NULL) && ( iterator->id != id)) { // search and compare id
|
||||
iterator = iterator->next;
|
||||
}
|
||||
return( iterator);
|
||||
}
|
||||
|
||||
// Given an id, find it with the function above and then use node* value (target)
|
||||
// to create a gap of previous and next nodes which is then closed.
|
||||
// if id is not found nothing happens
|
||||
node *remove_node_by_id( node *head, u32 id)
|
||||
{
|
||||
node *target = NULL;
|
||||
|
||||
target = find_id( head, id);
|
||||
if( target == NULL) {
|
||||
return( head);
|
||||
}
|
||||
head = zip_list( head, target);
|
||||
free_node( target);
|
||||
return( head);
|
||||
}
|
||||
|
||||
// Given a specific lval node* target value, remove it from the list *head
|
||||
// and then close the gap created by removing target.
|
||||
node *zip_list( node *head, node* target)
|
||||
{
|
||||
bool has_previous = true;
|
||||
bool has_next = true;
|
||||
|
||||
if( target == NULL) {
|
||||
return( head);
|
||||
}
|
||||
if( target->previous == NULL) {
|
||||
has_previous = false;
|
||||
#ifdef KDEBUG
|
||||
kprint("has_previous FALSE\n");
|
||||
#endif
|
||||
}
|
||||
if ( target->next == NULL) {
|
||||
has_next = false;
|
||||
#ifdef KDEBUG
|
||||
kprint("has_next FALSE\n");
|
||||
#endif
|
||||
}
|
||||
if( (has_next == true) && (has_previous == true)) { // Node in middle of chain
|
||||
target->previous->next = target->next;
|
||||
target->next->previous = target->previous;
|
||||
} else if ( (has_next == false && has_previous == true)) { // Node at end of chain
|
||||
target->previous->next = NULL;
|
||||
} else if ( (has_next == true && has_previous == false)) { // Node at start of chain
|
||||
target->next->previous = NULL;
|
||||
head = head->next;
|
||||
} else { // ( has_next == false && has_previous == false) // Node is isolated
|
||||
head = NULL;
|
||||
}
|
||||
return(head);
|
||||
}
|
||||
|
||||
// Use find_id to get the insert_point
|
||||
// decide if insertion is before or after
|
||||
// if ft_before_after = FALSE then insert before target
|
||||
// if ft_before_after = TRUE then append after target
|
||||
node *insert_node( node *head, node* insert_point, node *new_node, bool ft_before_after)
|
||||
{
|
||||
if( new_node == NULL) { // Nothing to add so return unchanged
|
||||
return( head);
|
||||
}
|
||||
if( insert_point == NULL) { // If no point specified either place before head or after tail
|
||||
if( ft_before_after) { // if after is set append as new tail
|
||||
head = append_node( head, new_node);
|
||||
} else { // if before than insert before head as new head
|
||||
new_node->next = head;
|
||||
new_node->previous = NULL;
|
||||
head-> previous = new_node;
|
||||
return( new_node); // return new head
|
||||
}
|
||||
return( head);
|
||||
}
|
||||
|
||||
if( ft_before_after) { // if adding after then...
|
||||
if( insert_point->next != NULL) { // insert node after insertion_point
|
||||
insert_point->next->previous = new_node;
|
||||
new_node->next = insert_point->next;
|
||||
} else { // insertion point is last node instead, skip above operations
|
||||
new_node->next = NULL;
|
||||
}
|
||||
new_node->previous = insert_point;
|
||||
insert_point->next = new_node;
|
||||
} else {
|
||||
if( insert_point->previous != NULL) { // insert node before insertion point
|
||||
insert_point->previous->next = new_node;
|
||||
new_node->previous = insert_point->previous;
|
||||
} else { // insertion point is head
|
||||
new_node->previous = NULL;
|
||||
head = new_node; // set new head as insertion point
|
||||
}
|
||||
insert_point->previous = new_node;
|
||||
new_node->next = insert_point;
|
||||
}
|
||||
return(head); // return changes (if any) to head
|
||||
}
|
||||
|
||||
node *get_tail( node *head)
|
||||
{
|
||||
if( head == NULL) {
|
||||
return(head);
|
||||
}
|
||||
node *tail = head;
|
||||
while( tail->next != NULL) { // iterate until the next node is not NULL
|
||||
tail = tail->next;
|
||||
}
|
||||
return( tail);
|
||||
}
|
||||
|
||||
void get_min_max_id( node *head, u32 *min, u32 *max)
|
||||
{
|
||||
if( head == NULL) {
|
||||
return;
|
||||
}
|
||||
node *iterator = head;
|
||||
*min = 0xFFFFFFFF;
|
||||
*max = 0;
|
||||
|
||||
while( iterator != NULL) { // iterate until the next node is not NULL
|
||||
if( *min > iterator->id) {
|
||||
*min = iterator->id;
|
||||
}
|
||||
if( *max < iterator->id) {
|
||||
*max = iterator->id;
|
||||
}
|
||||
iterator = iterator->next;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void swap_node_data( node* left, node *right)
|
||||
{
|
||||
if(( left == NULL) || (right == NULL)) {
|
||||
kprint("Invalid element.\n");
|
||||
return;
|
||||
}
|
||||
node swap;
|
||||
|
||||
kprint_hex("SWAP ", left->id, 4);
|
||||
kprint_hex(" with ", right->id, 4);
|
||||
kprint("\n");
|
||||
|
||||
swap.id = left->id;
|
||||
swap.base_register = left->base_register;
|
||||
swap.limit_register = left->limit_register;
|
||||
swap.ft_hole_mem = left->ft_hole_mem;
|
||||
|
||||
left->id = right->id;
|
||||
left->base_register = right->base_register;
|
||||
left->limit_register = right->limit_register;
|
||||
left->ft_hole_mem = right->ft_hole_mem;
|
||||
|
||||
right->id = swap.id;
|
||||
right->base_register = swap.base_register;
|
||||
right->limit_register = swap.limit_register;
|
||||
right->ft_hole_mem = swap.ft_hole_mem;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
node *swap_nodes( node *head, node** left, node **right)
|
||||
{
|
||||
|
||||
if( head == NULL) { // don't swap nodes in a list that is empty
|
||||
return( head);
|
||||
}
|
||||
|
||||
if( (*left == NULL) || (*right == NULL) ) { // if either or both nodes are NULL do nothing
|
||||
return( head);
|
||||
}
|
||||
if ( head == *left) { // if left happens to be head than set head to right
|
||||
head = *right;
|
||||
}
|
||||
if ( head == *right) { // if right happens to be head then set head to left
|
||||
head = *left;
|
||||
}
|
||||
|
||||
node *left_previous = (*left)->previous;
|
||||
node *left_next = (*left)->next;
|
||||
node *right_previous = (*right)->previous;
|
||||
node *right_next = (*right)->next;
|
||||
|
||||
if( (*left)->previous != NULL) { // make sure not to access a NULL value node
|
||||
(*left)->previous->next = *right; // previous node to left now points to right instead of left
|
||||
}
|
||||
|
||||
if( (*left)->next != NULL) { // same
|
||||
(*left)->next->previous = *right; // next node to left now points to right instead of left
|
||||
}
|
||||
|
||||
if( (*right)->previous != NULL) { // same
|
||||
(*right)->previous->next = *left; // same as above but right previous to left now
|
||||
}
|
||||
if( (*right)->next != NULL) { // same
|
||||
(*right)->next->previous = *left; // same as above...
|
||||
}
|
||||
|
||||
(*left)->previous = right_previous; // this will obliterate left->previous so it is preserved in left_previous
|
||||
(*left)->next = right_next; // this will obliterate left->next so it is preserved in left_next
|
||||
|
||||
(*right)->previous = left_previous; // now point right node back to previous to left node (obliterated above)
|
||||
(*right)->next = left_next; // now point right node forward to next of left node (obliterated above)
|
||||
|
||||
node* swap;
|
||||
swap = *left;
|
||||
*left = *right;
|
||||
*right = swap;
|
||||
|
||||
#ifdef KDEBUG
|
||||
kprint("LEFT: ");
|
||||
print_node( *left);
|
||||
kprint(" RIGHT: ");
|
||||
print_node( *right);
|
||||
kprint("\n");
|
||||
#endif
|
||||
|
||||
return( head); // return new head if it changed
|
||||
}
|
||||
|
||||
// good ways to sort a list, transfer it to an array and merge or quicksort it
|
||||
// not so good bubble-sort, insertion, select-sort, heap-sort
|
||||
// worst, this ugly hack
|
||||
node *hacksort_list( node* head, bool ft_descending_ascending)
|
||||
{
|
||||
if( head == NULL) {
|
||||
kprint("LIST EMPTY\n");
|
||||
return(head);
|
||||
}
|
||||
if( ft_descending_ascending) {
|
||||
kprint("ASCENDING\n");
|
||||
} else {
|
||||
kprint("DESCENDING\n");
|
||||
}
|
||||
node* outer_iterator = head;
|
||||
node* inner_iterator = head->next;
|
||||
node* max = head;
|
||||
|
||||
while( outer_iterator != NULL) {
|
||||
max = outer_iterator;
|
||||
inner_iterator = outer_iterator->next;
|
||||
while( inner_iterator != NULL) {
|
||||
if( ft_descending_ascending) {
|
||||
if( max->id > inner_iterator->id) { // This decides ASCENDING
|
||||
max = inner_iterator;
|
||||
}
|
||||
} else {
|
||||
if( max->id < inner_iterator->id) { // This decides SORT DESCENDING
|
||||
max = inner_iterator;
|
||||
}
|
||||
}
|
||||
inner_iterator = inner_iterator->next;
|
||||
/******* STOP COUNTER for Infnite Loop Halting
|
||||
u32 count = 0;
|
||||
if( count++ > 10) {
|
||||
return(head);
|
||||
}
|
||||
****/
|
||||
}
|
||||
if( ( outer_iterator != NULL) && ( outer_iterator != max)) {
|
||||
swap_node_data( outer_iterator, max);
|
||||
}
|
||||
outer_iterator = outer_iterator->next;
|
||||
}
|
||||
return( head);
|
||||
}
|
||||
|
||||
node *mergesort_list( node* head, node *pivot, node *left, node *right)
|
||||
{
|
||||
return( head); // disabled, not yet implemented
|
||||
if( ( head == NULL) || ( pivot == NULL) | ( left == NULL) | ( right == NULL)) {
|
||||
return( head);
|
||||
}
|
||||
node *start = left;
|
||||
node *end = right;
|
||||
while( (start->next != NULL) || (start->id != end->id)) {
|
||||
if( start->id < pivot->id) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
return( head);
|
||||
}
|
||||
|
||||
// stand in for delete, this only zeroes all the values of the node allocated by kmalloc
|
||||
// the memory is not reclaimed, need to add a tracker for these kmalloc allocated blocks
|
||||
// to implement a proper delete function
|
||||
void free_node( node *target)
|
||||
{
|
||||
memory_set( (u8 *) target, 0, NODE_SIZE);
|
||||
target = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// Print a single node's values
|
||||
void print_node( node *current)
|
||||
{
|
||||
char numstr[16];
|
||||
|
||||
if( current->previous != NULL) {
|
||||
kprint_hex( "(", current->previous->id, 4);
|
||||
kprint( ")<-");
|
||||
} else {
|
||||
kprint("NULL<-");
|
||||
}
|
||||
|
||||
memory_set( (u8 *) numstr, 0, 16);
|
||||
hex_to_ascii( current->id, numstr, 16);
|
||||
kprint("ID: "); kprint( numstr); kprint(" --- ");
|
||||
memory_set( (u8 *) numstr, 0, 16);
|
||||
hex_to_ascii( current->base_register, numstr, 16);
|
||||
kprint("BASE: "); kprint( numstr); kprint(" --- ");
|
||||
memory_set( (u8 *) numstr, 0, 16);
|
||||
hex_to_ascii( current->limit_register, numstr, 16);
|
||||
kprint("LIMIT: "); kprint( numstr); kprint(" --- ");
|
||||
if( current->ft_hole_mem) {
|
||||
kprint("MEMORY ");
|
||||
} else {
|
||||
kprint("HOLE ");
|
||||
}
|
||||
if( current->next != NULL) {
|
||||
kprint_hex( "->", current->next->id, 4);
|
||||
} else {
|
||||
kprint("->NULL");
|
||||
}
|
||||
kprint("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// just print the id separated by commas with a line-feed at the end
|
||||
void shortprint_list( node *head, bool ft_descending_ascending)
|
||||
{
|
||||
if( head == NULL) {
|
||||
kprint( "EMPTY.");
|
||||
kprint("\n");
|
||||
return;
|
||||
}
|
||||
node *iterator = NULL;
|
||||
if( ft_descending_ascending) {
|
||||
iterator = head;
|
||||
while( iterator != NULL) {
|
||||
kprint_hex( ",",iterator->id, 4);
|
||||
iterator = iterator->next;
|
||||
}
|
||||
} else {
|
||||
iterator = get_tail( head);
|
||||
while( iterator != NULL) {
|
||||
kprint_hex( ",",iterator->id, 4);
|
||||
iterator = iterator->previous;
|
||||
}
|
||||
}
|
||||
kprint("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// iterate through a list printing out the values of each node
|
||||
void print_list( node *head, bool ft_descending_ascending)
|
||||
{
|
||||
if( head == NULL) {
|
||||
kprint( "EMPTY.");
|
||||
return;
|
||||
}
|
||||
node *iterator = NULL;
|
||||
if( ft_descending_ascending) {
|
||||
iterator = head;
|
||||
while( iterator != NULL) {
|
||||
print_node( iterator);
|
||||
iterator = iterator->next;
|
||||
}
|
||||
} else {
|
||||
iterator = get_tail( head);
|
||||
while( iterator != NULL) {
|
||||
print_node( iterator);
|
||||
iterator = iterator->previous;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
27
Part3/09_memory/libc/linked.h
Normal file
27
Part3/09_memory/libc/linked.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef LINKED_H
|
||||
#define LINKED_H
|
||||
|
||||
#include "../cpu/types.h"
|
||||
#include "globals.h"
|
||||
#include "mem.h"
|
||||
|
||||
node *create_node( u32 base_register, u32 limit_register, bool ft_hole_mem, u32 id);
|
||||
node *add_node( node *head, u32 base_register, u32 limit_register, bool ft_hole_mem, u32 id);
|
||||
node *_add_node( node *head, node *new_node);
|
||||
node *append_node( node *head, node* new_node);
|
||||
node *find_id( node *head, u32 id);
|
||||
node *remove_node_by_id( node *head, u32 id);
|
||||
node *zip_list( node *head, node* target);
|
||||
node *insert_node( node *head, node* insert_point, node *new_node, bool ft_before_after);
|
||||
node *get_tail( node *head);
|
||||
void get_min_max_id( node *head, u32 *min, u32 *max);
|
||||
node *swap_nodes( node *head, node **left, node **right);
|
||||
void swap_node_data( node* left, node *right);
|
||||
node *hacksort_list( node* head, bool ft_backward_forward);
|
||||
node *mergesort_list( node* head, node *pivot, node *left, node *right);
|
||||
void free_node( node *target);
|
||||
void print_node( node *current);
|
||||
void print_list( node *head, bool ft_backward_forward);
|
||||
void shortprint_list( node *head, bool ft_backward_forward);
|
||||
|
||||
#endif
|
76
Part3/09_memory/libc/mem.c
Normal file
76
Part3/09_memory/libc/mem.c
Normal file
@ -0,0 +1,76 @@
|
||||
#include "mem.h"
|
||||
|
||||
void memory_copy(u8 *source, u8 *dest, int nbytes) {
|
||||
int i;
|
||||
for (i = 0; i < nbytes; i++) {
|
||||
*(dest + i) = *(source + i);
|
||||
}
|
||||
}
|
||||
|
||||
void memory_set(u8 *dest, u8 val, u32 len) {
|
||||
u8 *temp = (u8 *)dest;
|
||||
for ( ; len != 0; len--) *temp++ = val;
|
||||
}
|
||||
|
||||
// See globals.h for kmem_addr global declaration
|
||||
// kmem_addr is reserved block of memory for kernel to store linked list nodes
|
||||
/* Implementation is just a pointer to some free memory which
|
||||
* keeps growing */
|
||||
u32 kmalloc(u32 size, int align, u32 *phys_addr) {
|
||||
/* Pages are aligned to 4K, or 0x1000 */
|
||||
if (align == 1 && (kmem_addr & 0xFFFFF000)) {
|
||||
kmem_addr &= 0xFFFFF000;
|
||||
kmem_addr += 0x1000;
|
||||
}
|
||||
|
||||
|
||||
char c[16];
|
||||
hex_to_ascii( *phys_addr, c, 16);
|
||||
kprint("Inside kmalloc phys_addr (before) = ");
|
||||
kprint(c);
|
||||
kprint(" --- ");
|
||||
|
||||
/* Save the physical address */
|
||||
*phys_addr = kmem_addr;
|
||||
|
||||
hex_to_ascii( *phys_addr, c, 16);
|
||||
kprint("(after) phys_addr = ");
|
||||
kprint(c);
|
||||
kprint("\n");
|
||||
|
||||
u32 ret = kmem_addr;
|
||||
kmem_addr += size; /* Remember to increment the pointer */
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// See globals.h for user_start global declaration
|
||||
// umem_addr is reserved block of memory for kernel to store linked list nodes
|
||||
/* Implementation is just a pointer to some free memory which
|
||||
* keeps growing */
|
||||
u32 umalloc(u32 size, int align, u32 *phys_addr) {
|
||||
/* Pages are aligned to 4K, or 0x1000 */
|
||||
if (align == 1 && (umem_addr & 0xFFFFF000)) {
|
||||
umem_addr &= 0xFFFFF000;
|
||||
umem_addr += 0x1000;
|
||||
}
|
||||
|
||||
|
||||
char c[16];
|
||||
hex_to_ascii( *phys_addr, c, 16);
|
||||
kprint("Inside umalloc phys_addr (before) = ");
|
||||
kprint(c);
|
||||
kprint(" --- ");
|
||||
|
||||
/* Save the physical address */
|
||||
*phys_addr = umem_addr;
|
||||
|
||||
hex_to_ascii( *phys_addr, c, 16);
|
||||
kprint("(after) phys_addr = ");
|
||||
kprint(c);
|
||||
kprint("\n");
|
||||
|
||||
u32 ret = umem_addr;
|
||||
umem_addr += size; /* Remember to increment the pointer */
|
||||
return ret;
|
||||
}
|
15
Part3/09_memory/libc/mem.h
Normal file
15
Part3/09_memory/libc/mem.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef MEM_H
|
||||
#define MEM_H
|
||||
|
||||
#include "../cpu/types.h"
|
||||
#include "../libc/string.h"
|
||||
#include "../drivers/screen.h"
|
||||
#include "globals.h"
|
||||
|
||||
void memory_copy(u8 *source, u8 *dest, int nbytes);
|
||||
void memory_set(u8 *dest, u8 val, u32 len);
|
||||
|
||||
u32 kmalloc(u32 size, int align, u32 *phys_addr);
|
||||
u32 umalloc(u32 size, int align, u32 *phys_addr);
|
||||
|
||||
#endif
|
112
Part3/09_memory/libc/string.c
Normal file
112
Part3/09_memory/libc/string.c
Normal file
@ -0,0 +1,112 @@
|
||||
#include "string.h"
|
||||
|
||||
/**
|
||||
* K&R implementation
|
||||
*/
|
||||
void int_to_ascii(int n, char str[], int size) {
|
||||
memory_set( (u8 *) str, 0, size);
|
||||
int i, sign;
|
||||
if ((sign = n) < 0) n = -n;
|
||||
i = 0;
|
||||
do {
|
||||
str[i++] = n % 10 + '0';
|
||||
} while ((n /= 10) > 0);
|
||||
|
||||
if (sign < 0) str[i++] = '-';
|
||||
str[i] = '\0';
|
||||
|
||||
reverse(str);
|
||||
}
|
||||
|
||||
// Convert the numeric value n to a character string of 0x#...# where # is hexadecimal values
|
||||
// Size is the size of the char str[] array so number of hex digits is 1 less than size
|
||||
// because you need a terminating 0
|
||||
// the char str[] is erased with 0 values at start.
|
||||
void hex_to_ascii(int n, char str[], int size) {
|
||||
memory_set( (u8 *) str, 0, size);
|
||||
append(str, '0');
|
||||
append(str, 'x');
|
||||
char zeros = 0;
|
||||
|
||||
s32 tmp;
|
||||
int i;
|
||||
|
||||
for (i = 28; i > 0; i -= 4) {
|
||||
tmp = (n >> i) & 0xF;
|
||||
if (tmp == 0 && zeros == 0) continue;
|
||||
zeros = 1;
|
||||
if (tmp >= 0xA) append(str, tmp - 0xA + 'A');
|
||||
else append(str, tmp + '0');
|
||||
}
|
||||
|
||||
tmp = n & 0xF;
|
||||
if (tmp >= 0xA) append(str, tmp - 0xA + 'A');
|
||||
else append(str, tmp + '0');
|
||||
}
|
||||
|
||||
/* K&R */
|
||||
void reverse(char s[]) {
|
||||
int c, i, j;
|
||||
for (i = 0, j = strlen(s)-1; i < j; i++, j--) {
|
||||
c = s[i];
|
||||
s[i] = s[j];
|
||||
s[j] = c;
|
||||
}
|
||||
}
|
||||
|
||||
/* K&R */
|
||||
int strlen(char s[]) {
|
||||
int i = 0;
|
||||
while (s[i] != '\0') ++i;
|
||||
return i;
|
||||
}
|
||||
|
||||
// not K&R
|
||||
int sstrlen( char s[], u32 size)
|
||||
{
|
||||
if( size <= 0) {
|
||||
return(0);
|
||||
}
|
||||
u32 i = 0;
|
||||
while ( (s[i] != '\0') && ( i < size)) { ++i; }
|
||||
return( i);
|
||||
}
|
||||
|
||||
void append(char s[], char n) {
|
||||
int len = strlen(s);
|
||||
s[len] = n;
|
||||
s[len+1] = '\0';
|
||||
}
|
||||
|
||||
void backspace(char s[]) {
|
||||
int len = strlen(s);
|
||||
s[len-1] = '\0';
|
||||
}
|
||||
|
||||
/* K&R
|
||||
* Returns <0 if s1<s2, 0 if s1==s2, >0 if s1>s2 */
|
||||
int strcmp(char s1[], char s2[]) {
|
||||
int i;
|
||||
for (i = 0; s1[i] == s2[i]; i++) {
|
||||
if (s1[i] == '\0') return 0;
|
||||
}
|
||||
return s1[i] - s2[i];
|
||||
}
|
||||
|
||||
// Not K&R
|
||||
// Compare strings for first size characters
|
||||
// if size > both string sizes compare whole string
|
||||
int strncmp( char s1[], char s2[], u32 size)
|
||||
{
|
||||
u32 i = 0;
|
||||
while( (s1[i] != '\0') && (s2[i] != '\0') && (i < size) && (s1[i] == s2[i])) {
|
||||
++i;
|
||||
}
|
||||
if(i >= size) { // match exceeded size, they are same up to size chars
|
||||
return(0);
|
||||
} else if ( (s1[i] == s2[i])) { // match ended before size are they same? (includeing NULL matches)
|
||||
return(0);
|
||||
} // else don't match return diff
|
||||
return( s1[i] - s2[i]);
|
||||
}
|
||||
|
17
Part3/09_memory/libc/string.h
Normal file
17
Part3/09_memory/libc/string.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef STRINGS_H
|
||||
#define STRINGS_H
|
||||
|
||||
#include "../cpu/types.h"
|
||||
#include "mem.h"
|
||||
|
||||
void int_to_ascii(int n, char str[], int size);
|
||||
void hex_to_ascii(int n, char str[], int size);
|
||||
void reverse(char s[]);
|
||||
int strlen(char s[]);
|
||||
void backspace(char s[]);
|
||||
void append(char s[], char n);
|
||||
int strcmp(char s1[], char s2[]);
|
||||
int strncmp(char s1[], char s2[], u32 size);
|
||||
int sstrlen( char s1[], u32 size);
|
||||
|
||||
#endif
|
BIN
Part3/CS3502S01_Project_III_MemoryManagement.docx
Normal file
BIN
Part3/CS3502S01_Project_III_MemoryManagement.docx
Normal file
Binary file not shown.
Reference in New Issue
Block a user