From 418bb11305c7e3dd04358d78ba67ffa08236f1eb Mon Sep 17 00:00:00 2001 From: Carlos Fenollosa Date: Tue, 3 Feb 2015 19:12:51 +0100 Subject: [PATCH] Lesson 18 --- 14-checkpoint/boot/32bit_print.asm | 27 ++++++++++++++++- 14-checkpoint/boot/disk.asm | 47 ++++++++++++++++++++++++++++- 14-checkpoint/boot/gdt.asm | 36 +++++++++++++++++++++- 14-checkpoint/boot/kernel_entry.asm | 5 ++- 14-checkpoint/boot/print.asm | 38 ++++++++++++++++++++++- 14-checkpoint/boot/print_hex.asm | 47 ++++++++++++++++++++++++++++- 14-checkpoint/boot/switch_pm.asm | 23 +++++++++++++- 15-video-ports/Makefile | 47 ++++++++++++++++++++++++++++- 15-video-ports/boot | 1 - 16-video-driver/Makefile | 47 ++++++++++++++++++++++++++++- 16-video-driver/boot | 1 - 17-video-scroll/Makefile | 47 ++++++++++++++++++++++++++++- 17-video-scroll/boot | 1 - 17-video-scroll/drivers/ports.c | 36 +++++++++++++++++++++- 17-video-scroll/drivers/ports.h | 5 ++- 17-video-scroll/drivers/screen.h | 15 ++++++++- 18-interrupts/kernel/util.h | 5 +++ 17 files changed, 412 insertions(+), 16 deletions(-) mode change 120000 => 100644 14-checkpoint/boot/32bit_print.asm mode change 120000 => 100644 14-checkpoint/boot/disk.asm mode change 120000 => 100644 14-checkpoint/boot/gdt.asm mode change 120000 => 100644 14-checkpoint/boot/kernel_entry.asm mode change 120000 => 100644 14-checkpoint/boot/print.asm mode change 120000 => 100644 14-checkpoint/boot/print_hex.asm mode change 120000 => 100644 14-checkpoint/boot/switch_pm.asm mode change 120000 => 100644 15-video-ports/Makefile delete mode 120000 15-video-ports/boot mode change 120000 => 100644 16-video-driver/Makefile delete mode 120000 16-video-driver/boot mode change 120000 => 100644 17-video-scroll/Makefile delete mode 120000 17-video-scroll/boot mode change 120000 => 100644 17-video-scroll/drivers/ports.c mode change 120000 => 100644 17-video-scroll/drivers/ports.h mode change 120000 => 100644 17-video-scroll/drivers/screen.h diff --git a/14-checkpoint/boot/32bit_print.asm b/14-checkpoint/boot/32bit_print.asm deleted file mode 120000 index b249c3a..0000000 --- a/14-checkpoint/boot/32bit_print.asm +++ /dev/null @@ -1 +0,0 @@ -../../08-32bit-print/32bit-print.asm \ No newline at end of file diff --git a/14-checkpoint/boot/32bit_print.asm b/14-checkpoint/boot/32bit_print.asm new file mode 100644 index 0000000..4169a17 --- /dev/null +++ b/14-checkpoint/boot/32bit_print.asm @@ -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 diff --git a/14-checkpoint/boot/disk.asm b/14-checkpoint/boot/disk.asm deleted file mode 120000 index b01433e..0000000 --- a/14-checkpoint/boot/disk.asm +++ /dev/null @@ -1 +0,0 @@ -../../07-bootsector-disk/boot_sect_disk.asm \ No newline at end of file diff --git a/14-checkpoint/boot/disk.asm b/14-checkpoint/boot/disk.asm new file mode 100644 index 0000000..be3d0a9 --- /dev/null +++ b/14-checkpoint/boot/disk.asm @@ -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 diff --git a/14-checkpoint/boot/gdt.asm b/14-checkpoint/boot/gdt.asm deleted file mode 120000 index 3ebae1a..0000000 --- a/14-checkpoint/boot/gdt.asm +++ /dev/null @@ -1 +0,0 @@ -../../09-32bit-gdt/32bit-gdt.asm \ No newline at end of file diff --git a/14-checkpoint/boot/gdt.asm b/14-checkpoint/boot/gdt.asm new file mode 100644 index 0000000..e5db388 --- /dev/null +++ b/14-checkpoint/boot/gdt.asm @@ -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 diff --git a/14-checkpoint/boot/kernel_entry.asm b/14-checkpoint/boot/kernel_entry.asm deleted file mode 120000 index 387cb0d..0000000 --- a/14-checkpoint/boot/kernel_entry.asm +++ /dev/null @@ -1 +0,0 @@ -../../13-kernel-barebones/kernel_entry.asm \ No newline at end of file diff --git a/14-checkpoint/boot/kernel_entry.asm b/14-checkpoint/boot/kernel_entry.asm new file mode 100644 index 0000000..6728303 --- /dev/null +++ b/14-checkpoint/boot/kernel_entry.asm @@ -0,0 +1,4 @@ +[bits 32] +[extern main] ; Define calling point. Must have same name as kernel.c 'main' function +call main ; Calls the C function. The linker will know where it is placed in memory +jmp $ diff --git a/14-checkpoint/boot/print.asm b/14-checkpoint/boot/print.asm deleted file mode 120000 index 045ec24..0000000 --- a/14-checkpoint/boot/print.asm +++ /dev/null @@ -1 +0,0 @@ -../../05-bootsector-functions-strings/boot_sect_print.asm \ No newline at end of file diff --git a/14-checkpoint/boot/print.asm b/14-checkpoint/boot/print.asm new file mode 100644 index 0000000..b066e0d --- /dev/null +++ b/14-checkpoint/boot/print.asm @@ -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 diff --git a/14-checkpoint/boot/print_hex.asm b/14-checkpoint/boot/print_hex.asm deleted file mode 120000 index 2583a6d..0000000 --- a/14-checkpoint/boot/print_hex.asm +++ /dev/null @@ -1 +0,0 @@ -../../05-bootsector-functions-strings/boot_sect_print_hex.asm \ No newline at end of file diff --git a/14-checkpoint/boot/print_hex.asm b/14-checkpoint/boot/print_hex.asm new file mode 100644 index 0000000..b537ef8 --- /dev/null +++ b/14-checkpoint/boot/print_hex.asm @@ -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 diff --git a/14-checkpoint/boot/switch_pm.asm b/14-checkpoint/boot/switch_pm.asm deleted file mode 120000 index 3eac90c..0000000 --- a/14-checkpoint/boot/switch_pm.asm +++ /dev/null @@ -1 +0,0 @@ -../../10-32bit-enter/32bit-switch.asm \ No newline at end of file diff --git a/14-checkpoint/boot/switch_pm.asm b/14-checkpoint/boot/switch_pm.asm new file mode 100644 index 0000000..9394da3 --- /dev/null +++ b/14-checkpoint/boot/switch_pm.asm @@ -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 diff --git a/15-video-ports/Makefile b/15-video-ports/Makefile deleted file mode 120000 index 3fbcd60..0000000 --- a/15-video-ports/Makefile +++ /dev/null @@ -1 +0,0 @@ -../14-checkpoint/Makefile \ No newline at end of file diff --git a/15-video-ports/Makefile b/15-video-ports/Makefile new file mode 100644 index 0000000..b611257 --- /dev/null +++ b/15-video-ports/Makefile @@ -0,0 +1,46 @@ +C_SOURCES = $(wildcard kernel/*.c drivers/*.c) +HEADERS = $(wildcard kernel/*.h drivers/*.h) +# Nice syntax for file extension replacement +OBJ = ${C_SOURCES:.c=.o} + +# Change this if your cross-compiler is somewhere else +CC = /usr/local/i386elfgcc/bin/i386-elf-gcc +GDB = /usr/local/i386elfgcc/bin/i386-elf-gdb +# -g: Use debugging symbols in gcc +CFLAGS = -g + +# 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} + i386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary + +# Used for debugging purposes +kernel.elf: boot/kernel_entry.o ${OBJ} + i386-elf-ld -o $@ -Ttext 0x1000 $^ + +run: os-image.bin + qemu-system-i386 -fda os-image.bin + +# 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 & + ${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} -ffreestanding -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 diff --git a/15-video-ports/boot b/15-video-ports/boot deleted file mode 120000 index 7ce5a3d..0000000 --- a/15-video-ports/boot +++ /dev/null @@ -1 +0,0 @@ -../14-checkpoint/boot \ No newline at end of file diff --git a/16-video-driver/Makefile b/16-video-driver/Makefile deleted file mode 120000 index 3fbcd60..0000000 --- a/16-video-driver/Makefile +++ /dev/null @@ -1 +0,0 @@ -../14-checkpoint/Makefile \ No newline at end of file diff --git a/16-video-driver/Makefile b/16-video-driver/Makefile new file mode 100644 index 0000000..b611257 --- /dev/null +++ b/16-video-driver/Makefile @@ -0,0 +1,46 @@ +C_SOURCES = $(wildcard kernel/*.c drivers/*.c) +HEADERS = $(wildcard kernel/*.h drivers/*.h) +# Nice syntax for file extension replacement +OBJ = ${C_SOURCES:.c=.o} + +# Change this if your cross-compiler is somewhere else +CC = /usr/local/i386elfgcc/bin/i386-elf-gcc +GDB = /usr/local/i386elfgcc/bin/i386-elf-gdb +# -g: Use debugging symbols in gcc +CFLAGS = -g + +# 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} + i386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary + +# Used for debugging purposes +kernel.elf: boot/kernel_entry.o ${OBJ} + i386-elf-ld -o $@ -Ttext 0x1000 $^ + +run: os-image.bin + qemu-system-i386 -fda os-image.bin + +# 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 & + ${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} -ffreestanding -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 diff --git a/16-video-driver/boot b/16-video-driver/boot deleted file mode 120000 index 7ce5a3d..0000000 --- a/16-video-driver/boot +++ /dev/null @@ -1 +0,0 @@ -../14-checkpoint/boot \ No newline at end of file diff --git a/17-video-scroll/Makefile b/17-video-scroll/Makefile deleted file mode 120000 index 3fbcd60..0000000 --- a/17-video-scroll/Makefile +++ /dev/null @@ -1 +0,0 @@ -../14-checkpoint/Makefile \ No newline at end of file diff --git a/17-video-scroll/Makefile b/17-video-scroll/Makefile new file mode 100644 index 0000000..b611257 --- /dev/null +++ b/17-video-scroll/Makefile @@ -0,0 +1,46 @@ +C_SOURCES = $(wildcard kernel/*.c drivers/*.c) +HEADERS = $(wildcard kernel/*.h drivers/*.h) +# Nice syntax for file extension replacement +OBJ = ${C_SOURCES:.c=.o} + +# Change this if your cross-compiler is somewhere else +CC = /usr/local/i386elfgcc/bin/i386-elf-gcc +GDB = /usr/local/i386elfgcc/bin/i386-elf-gdb +# -g: Use debugging symbols in gcc +CFLAGS = -g + +# 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} + i386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary + +# Used for debugging purposes +kernel.elf: boot/kernel_entry.o ${OBJ} + i386-elf-ld -o $@ -Ttext 0x1000 $^ + +run: os-image.bin + qemu-system-i386 -fda os-image.bin + +# 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 & + ${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} -ffreestanding -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 diff --git a/17-video-scroll/boot b/17-video-scroll/boot deleted file mode 120000 index 85eedf8..0000000 --- a/17-video-scroll/boot +++ /dev/null @@ -1 +0,0 @@ -../14-checkpoint/boot/ \ No newline at end of file diff --git a/17-video-scroll/drivers/ports.c b/17-video-scroll/drivers/ports.c deleted file mode 120000 index 82c662a..0000000 --- a/17-video-scroll/drivers/ports.c +++ /dev/null @@ -1 +0,0 @@ -../../16-video-driver/drivers/ports.c \ No newline at end of file diff --git a/17-video-scroll/drivers/ports.c b/17-video-scroll/drivers/ports.c new file mode 100644 index 0000000..60c9b9a --- /dev/null +++ b/17-video-scroll/drivers/ports.c @@ -0,0 +1,35 @@ +/** + * Read a byte from the specified port + */ +unsigned char port_byte_in (unsigned short port) { + unsigned char 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 (unsigned short port, unsigned char 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__("out %%al, %%dx" : : "a" (data), "d" (port)); +} + +unsigned short port_word_in (unsigned short port) { + unsigned short result; + __asm__("in %%dx, %%ax" : "=a" (result) : "d" (port)); + return result; +} + +void port_word_out (unsigned short port, unsigned short data) { + __asm__("out %%ax, %%dx" : : "a" (data), "d" (port)); +} diff --git a/17-video-scroll/drivers/ports.h b/17-video-scroll/drivers/ports.h deleted file mode 120000 index 1de03ef..0000000 --- a/17-video-scroll/drivers/ports.h +++ /dev/null @@ -1 +0,0 @@ -../../16-video-driver/drivers/ports.h \ No newline at end of file diff --git a/17-video-scroll/drivers/ports.h b/17-video-scroll/drivers/ports.h new file mode 100644 index 0000000..db9253b --- /dev/null +++ b/17-video-scroll/drivers/ports.h @@ -0,0 +1,4 @@ +unsigned char port_byte_in (unsigned short port); +void port_byte_out (unsigned short port, unsigned char data); +unsigned short port_word_in (unsigned short port); +void port_word_out (unsigned short port, unsigned short data); diff --git a/17-video-scroll/drivers/screen.h b/17-video-scroll/drivers/screen.h deleted file mode 120000 index f304669..0000000 --- a/17-video-scroll/drivers/screen.h +++ /dev/null @@ -1 +0,0 @@ -../../16-video-driver/drivers/screen.h \ No newline at end of file diff --git a/17-video-scroll/drivers/screen.h b/17-video-scroll/drivers/screen.h new file mode 100644 index 0000000..61bc2e6 --- /dev/null +++ b/17-video-scroll/drivers/screen.h @@ -0,0 +1,14 @@ +#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); diff --git a/18-interrupts/kernel/util.h b/18-interrupts/kernel/util.h index defc436..3375159 100644 --- a/18-interrupts/kernel/util.h +++ b/18-interrupts/kernel/util.h @@ -1,5 +1,10 @@ +#ifndef UTIL_H +#define UTIL_H + #include "../cpu/types.h" void memory_copy(char *source, char *dest, int nbytes); void memory_set(u8 *dest, u8 val, u32 len); void int_to_ascii(int n, char str[]); + +#endif