├── oos.use ├── .bochsrc ├── sdk.use ├── include ├── stdbool.h ├── c_types.h ├── stdint.h ├── IDT.h └── GDT.h ├── isofs └── boot │ └── grub │ └── menu.lst ├── .bochsrc-dbg ├── src ├── devices │ ├── cpu │ │ ├── Registers.ooc │ │ ├── SysCall.ooc │ │ ├── cpu.asm │ │ ├── IDT.ooc │ │ ├── interrupts.asm │ │ ├── GDT.ooc │ │ ├── ISR.ooc │ │ └── IRQ.ooc │ ├── CPU.ooc │ ├── Ports.ooc │ ├── ports.asm │ ├── Display.ooc │ └── Keyboard.ooc ├── misc.asm ├── Kernel.ooc ├── linker.ld ├── Panic.ooc ├── memory │ ├── memory.asm │ └── MM.ooc ├── boot │ ├── main.ooc │ └── boot.asm ├── Bochs.ooc ├── Console.ooc ├── Multiboot.ooc └── Printf.ooc ├── .gitignore ├── sdk ├── lang │ ├── ooclib.ooc │ ├── README │ ├── array.h │ ├── memory.ooc │ └── types.ooc └── structs │ └── Bitmap.ooc ├── COPYING ├── Makefile └── README.markdown /oos.use: -------------------------------------------------------------------------------- 1 | Name: oos 2 | Description: ooc operating system 3 | SourcePath: src 4 | Main: boot/main.ooc 5 | -------------------------------------------------------------------------------- /.bochsrc: -------------------------------------------------------------------------------- 1 | boot: cdrom 2 | ata0-master: type=cdrom, path=oos.iso, status=inserted 3 | 4 | port_e9_hack: enabled=1 5 | -------------------------------------------------------------------------------- /sdk.use: -------------------------------------------------------------------------------- 1 | Name: sdk 2 | Description: The custom ooc sdk for oos 3 | SourcePath: sdk 4 | Imports: lang/ooclib, lang/types, lang/memory 5 | -------------------------------------------------------------------------------- /include/stdbool.h: -------------------------------------------------------------------------------- 1 | #ifndef STDBOOL_H 2 | #define STDBOOL_H 3 | 4 | #define bool _Bool 5 | #define true 1 6 | #define false 0 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /isofs/boot/grub/menu.lst: -------------------------------------------------------------------------------- 1 | default 0 2 | timeout 0 3 | color light-blue/black light-cyan/blue 4 | 5 | title oos 6 | kernel /system/oos.exe 7 | 8 | -------------------------------------------------------------------------------- /.bochsrc-dbg: -------------------------------------------------------------------------------- 1 | boot: cdrom 2 | ata0-master: type=cdrom, path=oos.iso, status=inserted 3 | 4 | gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0 5 | 6 | port_e9_hack: enabled=1 7 | -------------------------------------------------------------------------------- /include/c_types.h: -------------------------------------------------------------------------------- 1 | #ifndef C_TYPES_H 2 | #define C_TYPES_H 3 | // TODO: GET RID OF THIS FILE! __CHAR_ARY is a crappy hack to get j/ooc to cooperate 4 | #define __CHAR_ARY char* 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /src/devices/cpu/Registers.ooc: -------------------------------------------------------------------------------- 1 | Registers: cover { 2 | gs, fs, es, ds: UInt // segments 3 | edi, esi, ebp, esp, ebx, edx, ecx, eax: UInt // pushed by pusha 4 | interruptNumber, errorCode: UInt 5 | eip, cs, eflags, useresp, ss: UInt // pushed by the processor automatically 6 | } 7 | -------------------------------------------------------------------------------- /src/misc.asm: -------------------------------------------------------------------------------- 1 | bits 32 2 | 3 | section .text 4 | 5 | ;;; 6 | ;;; Miscellaneous 7 | ;;; 8 | 9 | ;;; stackDump: func 10 | extern stackDumpHex 11 | global stackDump 12 | stackDump: 13 | push ebp 14 | mov ebp, esp 15 | call stackDumpHex 16 | pop ebp 17 | ret 18 | -------------------------------------------------------------------------------- /src/devices/cpu/SysCall.ooc: -------------------------------------------------------------------------------- 1 | import IDT, Registers 2 | 3 | SysCall: cover { 4 | // in Exceptions.asm 5 | isrSyscall: extern proto static func 6 | 7 | setup: static func { 8 | IDT setGate(0x80, isrSyscall, 0x08, 0, 0, IDT INTR32) 9 | } 10 | 11 | handler: static func (regs: Registers@) { 12 | // TODO 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore the result of any builds. 2 | *.o 3 | *.sho 4 | *.exe 5 | *.lib 6 | *.shlib 7 | *.iso 8 | 9 | #ignore editor temporary and backup files 10 | .*.swp 11 | *~ 12 | \#*\# 13 | .\#* 14 | 15 | # Ignore build directory 16 | build 17 | 18 | # Ignore rock libcaching files 19 | .libs 20 | *.cacheinfo 21 | 22 | # Ignore the GRUB stage2 23 | stage2_eltorito 24 | 25 | -------------------------------------------------------------------------------- /sdk/lang/ooclib.ooc: -------------------------------------------------------------------------------- 1 | // variable arguments 2 | //version(gcc) { 3 | VaList: cover from __builtin_va_list 4 | va_start: extern(__builtin_va_start) func (VaList, ...) // ap, last_arg 5 | va_arg: extern(__builtin_va_arg) func (VaList, ...) -> Int // ap, type 6 | va_end: extern(__builtin_va_end) func (VaList) // ap 7 | va_copy: extern(__builtin_va_copy) func (VaList, VaList) // dest, src 8 | //} 9 | -------------------------------------------------------------------------------- /include/stdint.h: -------------------------------------------------------------------------------- 1 | #ifndef STDINT_H 2 | #define STDINT_H 3 | 4 | typedef unsigned long long uint64_t; 5 | typedef unsigned int uint32_t; 6 | typedef unsigned short uint16_t; 7 | typedef unsigned char uint8_t; 8 | 9 | typedef signed long long int64_t; 10 | typedef signed int int32_t; 11 | typedef signed short int16_t; 12 | typedef signed char int8_t; 13 | 14 | typedef unsigned int size_t; 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/devices/CPU.ooc: -------------------------------------------------------------------------------- 1 | import cpu/[GDT, IDT, ISR, IRQ, SysCall] 2 | 3 | CPU: cover { 4 | setup: static func { 5 | GDT setup() 6 | IDT setup() 7 | ISR setup() 8 | IRQ setup() 9 | SysCall setup() 10 | } 11 | 12 | enableInterrupts: extern proto static func 13 | disableInterrupts: extern proto static func 14 | 15 | halt: static extern proto func 16 | } 17 | -------------------------------------------------------------------------------- /include/IDT.h: -------------------------------------------------------------------------------- 1 | #ifndef IDT_H 2 | #define IDT_H 3 | 4 | #include 5 | 6 | /* IDT */ 7 | typedef struct IDTD { 8 | uint16_t size; 9 | uint32_t offset; 10 | } __attribute__((packed)) IDTD; 11 | 12 | typedef struct IDTG { 13 | uint16_t offset_1; 14 | uint16_t selector; 15 | uint8_t zero; 16 | uint8_t type_attr; 17 | uint16_t offset_2; 18 | } __attribute__((packed)) IDTG; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/devices/Ports.ooc: -------------------------------------------------------------------------------- 1 | Ports: cover { 2 | outByte: extern proto static func (port: UInt16, val: UInt8) 3 | inByte: extern proto static func (port: UInt16) -> UInt8 4 | 5 | outWord: extern proto static func (port: UInt16, val: UInt16) 6 | inWord: extern proto static func (port: UInt16) -> UInt16 7 | 8 | outLong: extern proto static func (port: UInt16, val: UInt32) 9 | inLong: extern proto static func (port: UInt16) -> UInt32 10 | } 11 | -------------------------------------------------------------------------------- /include/GDT.h: -------------------------------------------------------------------------------- 1 | #ifndef GDT_H 2 | #define GDT_H 3 | 4 | #include 5 | 6 | /* GDT */ 7 | typedef struct GDTD { 8 | uint16_t size; 9 | uint32_t offset; 10 | } __attribute__((packed)) GDTD; 11 | 12 | typedef struct GDTE { 13 | uint16_t limit_1; 14 | uint16_t base_1; 15 | uint8_t base_2; 16 | uint8_t access_byte; 17 | uint8_t flags__limit_2; 18 | uint8_t base_3; 19 | } __attribute__((packed)) GDTE; 20 | 21 | #endif /* GDT_H */ 22 | -------------------------------------------------------------------------------- /src/Kernel.ooc: -------------------------------------------------------------------------------- 1 | import devices/[CPU, Display, Keyboard], memory/MM 2 | 3 | Kernel: cover { 4 | setup: static func { 5 | Display setup() 6 | MM setup() 7 | CPU setup() 8 | Keyboard setup() 9 | CPU enableInterrupts() 10 | } 11 | 12 | start: static Pointer = kernelStart& 13 | end: static Pointer = kernelEnd& 14 | } 15 | 16 | // from the linker.ld linker script 17 | kernelStart: extern proto Int 18 | kernelEnd: extern proto Int 19 | -------------------------------------------------------------------------------- /src/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(boot) 2 | 3 | SECTIONS { 4 | /* 0x100000 = 1 MB. The kernel must be above 1 MB in order for GRUB to be able to load it. */ 5 | . = 0x100000; 6 | kernelStart = .; 7 | 8 | .text ALIGN (0x1000) : { 9 | *(.text) 10 | } 11 | 12 | .rodata ALIGN (0x1000) : { 13 | *(.rodata) 14 | } 15 | 16 | .data ALIGN (0x1000) : { 17 | *(.data) 18 | } 19 | 20 | .bss ALIGN (0x1000) : { 21 | *(COMMON) 22 | *(.bss) 23 | } 24 | 25 | kernelEnd = .; 26 | } 27 | -------------------------------------------------------------------------------- /sdk/lang/README: -------------------------------------------------------------------------------- 1 | lang package rules: 2 | - Every class outside lang.* import every lang.* class automatically 3 | - In lang.*, classes don't import each other automatically (to avoid circular dependencies) 4 | - All covers must be first defined in ooclib.ooc, then additional functions must go in their 5 | dedicated file (e.g. String.ooc, Char.ooc) 6 | 7 | Note: defining covers twice (or more!) is never a problem for the ooc compiler. 8 | The second, third, etc. definitions aren't allowed to add members / change the 9 | from-type, but should be used to add functions. 10 | -------------------------------------------------------------------------------- /src/Panic.ooc: -------------------------------------------------------------------------------- 1 | import Printf, devices/CPU 2 | 3 | panic: func(fmt: String, ...) { 4 | args: VaList 5 | 6 | va_start(args, fmt) 7 | 8 | "Panic: " print() 9 | vprintf(fmt, args) 10 | 11 | "\n\nStack trace:" println() 12 | stackDump() 13 | 14 | CPU disableInterrupts() 15 | CPU halt() 16 | } 17 | 18 | stackDump: extern proto func 19 | 20 | stackDumpHex: unmangled func (stack: UInt*) { 21 | originalStack := stack as UInt 22 | while(stack as UInt < originalStack align(0x1000)) { 23 | "\t%p: %p" printfln(stack, stack@) 24 | if(stack@ == 0x0) 25 | break 26 | stack += 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sdk/structs/Bitmap.ooc: -------------------------------------------------------------------------------- 1 | Bitmap: class { 2 | size: UInt 3 | data: UInt32* 4 | 5 | init: func (=size) { 6 | data = gc_malloc(UInt32 size * size) as UInt32* 7 | memset(data, 0 as UInt32, UInt32 size * size) 8 | } 9 | 10 | set: func (index, bit: UInt) { 11 | data[index] |= (1 << bit) 12 | } 13 | 14 | set?: func (index, bit: UInt) -> Bool { 15 | (data[index] & (1 << bit)) as Bool 16 | } 17 | 18 | // This is a quick way to check if all the bits in an element are set. 19 | allSet?: func (index: UInt) -> Bool { 20 | data[index] == 0xFFFFFFFF 21 | } 22 | 23 | clear: func (index, bit: UInt) { 24 | data[index] &= ~(1 << bit) 25 | } 26 | 27 | clear?: func (index, bit: UInt) -> Bool { 28 | !(data[index] & (1 << bit)) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/memory/memory.asm: -------------------------------------------------------------------------------- 1 | bits 32 2 | 3 | section .text 4 | 5 | ;;; 6 | ;;; Paging 7 | ;;; 8 | 9 | ;;; Stolen from OSDynamix: 10 | ;;; #define INVLPG(virt) ASMV("invlpg (%%eax)" : : "a" (virt)) 11 | ;;; #define SWITCH_ADDRESS_SPACE(x) ASMV("mov %0, %%CR3" : : "r" ((dword) (x))) 12 | 13 | ;;; invalidatePage: func (virt: Pointer) 14 | global invalidatePage 15 | invalidatePage: 16 | mov eax, [esp+4] ; virt 17 | invlpg [eax] 18 | ret 19 | 20 | ;;; switchAddressSpace: func (addressSpace: UInt*) 21 | global switchAddressSpace 22 | switchAddressSpace: 23 | mov eax, [esp+4] ; addressSpace 24 | mov cr3, eax 25 | ret 26 | 27 | ;;; getCR0: func -> SizeT 28 | global getCR0 29 | getCR0: 30 | mov eax, cr0 ; return value 31 | ret 32 | 33 | ;;; setCR0: func (cr0: SizeT) 34 | global setCR0 35 | setCR0: 36 | mov eax, [esp+4] ; cr0 37 | mov cr0, eax 38 | ret 39 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in 9 | all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 17 | THE SOFTWARE. 18 | -------------------------------------------------------------------------------- /sdk/lang/array.h: -------------------------------------------------------------------------------- 1 | #ifndef ___lang_array___ 2 | #define ___lang_array___ 3 | 4 | #include 5 | 6 | #define _lang_array__Array_new(type, size) ((_lang_array__Array) { size, lang_memory__gc_malloc(size * sizeof(type)) }); 7 | 8 | #define _lang_array__Array_get(array, index, type) ( \ 9 | (index < 0 || index >= array.length) ? \ 10 | lang_types__Exception_throw(lang_types__Exception_new_noOrigin(lang_types__String_format("when reading from array index = %d out of bounds [0, %d)\n", index, array.length))), \ 11 | *((type*) NULL) : \ 12 | ((type* restrict) array.data)[index]) 13 | 14 | #define _lang_array__Array_set(array, index, type, value) \ 15 | if(index < 0 || index >= array.length) { \ 16 | lang_types__Exception_throw(lang_types__Exception_new_noOrigin(lang_types__String_format("when writing to array index = %d out of bounds [0, %d)\n", index, array.length))); \ 17 | } \ 18 | ((type* restrict) array.data)[index] = value; 19 | 20 | typedef struct { 21 | size_t length; 22 | void* restrict data; 23 | } _lang_array__Array; 24 | 25 | #endif // ___lang_array___ 26 | -------------------------------------------------------------------------------- /src/boot/main.ooc: -------------------------------------------------------------------------------- 1 | import Kernel, Multiboot, devices/Display, memory/MM, Panic, Bochs, Console 2 | 3 | kmain: func (mb: MultibootInfo*, magic: UInt32) { 4 | multiboot = mb@ 5 | Kernel setup() 6 | 7 | "Welcome to " print() 8 | Display setFgColor(Color lightGreen, || 9 | "oos" print() 10 | ) 11 | " booted by " print() 12 | Display setFgColor(Color lightBlue, || 13 | multiboot bootLoaderName as String print() 14 | ) 15 | ".\n" println() 16 | 17 | "Total Memory: %6i kB" printfln(MM memorySize / 1024) 18 | "Used Memory: %6i kB" printfln(MM usedMemory / 1024) 19 | "Free Memory: %6i kB" printfln(MM freeMemory / 1024) 20 | '\n' print() 21 | 22 | "This is " print() 23 | Display setFgColor(Color lightRed, || 24 | "a super cool " print() 25 | Display setFgColor(Color red, || 26 | "example of " print() 27 | ) 28 | "the new " print() 29 | Display setBgColor(Color magenta, || 30 | "setFgColor and setBgColor" print() 31 | ) 32 | " functions.\n" println() 33 | ) 34 | 35 | Console run() 36 | } 37 | -------------------------------------------------------------------------------- /sdk/lang/memory.ooc: -------------------------------------------------------------------------------- 1 | import memory/MM 2 | 3 | // Memory allocation functions 4 | gc_malloc: func (size: SizeT) -> Pointer { 5 | MM alloc(size) 6 | } 7 | 8 | gc_malloc_atomic: func (size: SizeT) -> Pointer { 9 | gc_malloc(size) 10 | } 11 | 12 | gc_realloc: func (ptr: Pointer, size: SizeT) -> Pointer { 13 | null 14 | } 15 | 16 | gc_calloc: func (nmemb: SizeT, size: SizeT) -> Pointer { 17 | gc_malloc(nmemb * size) 18 | } 19 | 20 | free: func (ptr: Pointer) { 21 | MM free(ptr) 22 | } 23 | 24 | // Memory setting/copying functions 25 | zeroMemory: func (ptr: Pointer, count: SizeT) -> Pointer { 26 | memset(ptr, 0 as UInt8, count) 27 | } 28 | 29 | memset: func (dest: Pointer, val: T, count: SizeT) -> Pointer { 30 | destination := dest as T* 31 | 32 | for(i in 0..count) { 33 | destination[i] = val 34 | } 35 | 36 | return destination 37 | } 38 | 39 | memcpy: func (dest, src: Pointer, count: SizeT) -> Pointer { 40 | destination := dest as UInt8* 41 | source := src as UInt8* 42 | 43 | for(i in 0..count) { 44 | destination[i] = source[i] 45 | } 46 | 47 | return destination 48 | } 49 | -------------------------------------------------------------------------------- /src/devices/ports.asm: -------------------------------------------------------------------------------- 1 | bits 32 2 | 3 | section .text 4 | 5 | ;;; 6 | ;;; Ports 7 | ;;; 8 | 9 | ;;; outByte: func (port: UInt16, val: UInt8) 10 | global outByte 11 | outByte: 12 | mov eax, [esp+8] ; val 13 | mov edx, [esp+4] ; port 14 | out dx, al 15 | ret 16 | 17 | ;;; inByte: func (port: UInt16) -> UInt8 18 | global inByte 19 | inByte: 20 | mov edx, [esp+4] ; port 21 | in al, dx 22 | ret 23 | 24 | ;;; outWord: func (port: UInt16, val: UInt16) 25 | global outWord 26 | outWord: 27 | mov eax, [esp+8] ; val 28 | mov edx, [esp+4] ; port 29 | out dx, ax 30 | ret 31 | 32 | ;;; inWord: func (port: UInt16) -> UInt16 33 | global inWord 34 | inWord: 35 | mov edx, [esp+4] ; port 36 | in ax, dx 37 | ret 38 | 39 | ;;; outLong: func (port: UInt16, val: UInt32) 40 | global outLong 41 | outLong: 42 | mov eax, [esp+8] ; val 43 | mov edx, [esp+4] ; port 44 | out dx, eax 45 | ret 46 | 47 | ;;; inLong: func (port: UInt16) -> UInt32 48 | global inLong 49 | inLong: 50 | mov edx, [esp+4] ; port 51 | in eax, dx 52 | ret 53 | -------------------------------------------------------------------------------- /src/boot/boot.asm: -------------------------------------------------------------------------------- 1 | bits 32 2 | 3 | global boot ; making entry point visible to linker 4 | extern kmain ; kmain is defined in Main.ooc 5 | 6 | 7 | ;;; Multiboot header. See GRUB docs for details 8 | MODULEALIGN equ 1<<0 ; align loaded modules on page boundaries 9 | MEMINFO equ 1<<1 ; provide memory map 10 | FLAGS equ MODULEALIGN | MEMINFO ; this is the Multiboot 'flag' field 11 | MAGIC equ 0x1BADB002 ; 'magic number' lets bootloader find the header 12 | CHECKSUM equ -(MAGIC + FLAGS) ; checksum required 13 | 14 | 15 | section .text 16 | align 4 17 | 18 | MultiBootHeader: 19 | dd MAGIC 20 | dd FLAGS 21 | dd CHECKSUM 22 | 23 | ;;; The OS gains control right here 24 | boot: 25 | mov esp, stacktop ; set up the stack 26 | 27 | push eax ; pass Multiboot magic number 28 | push ebx ; pass Multiboot info structure 29 | 30 | call kmain ; call kernel proper 31 | 32 | cli ; disable interupts if kmain returns 33 | hang: 34 | hlt ; halt machine if kmain returns 35 | jmp hang 36 | 37 | 38 | section .bss 39 | align 4 40 | 41 | stack: 42 | resb 0x4000 ; reserve 16k for the stack on a doubleword boundary 43 | stacktop: ; the top of the stack is the bottom because the stack counts down 44 | -------------------------------------------------------------------------------- /src/devices/cpu/cpu.asm: -------------------------------------------------------------------------------- 1 | bits 32 2 | 3 | section .text 4 | 5 | ;;; loadGDT: func (GDTDescriptor*) 6 | global loadGDT 7 | loadGDT: 8 | push eax 9 | mov eax, [esp+0x8] ; get the struct pointer 10 | lgdt [eax] ; load the GDT 11 | pop eax 12 | ;; Reload CS register containing code selector: 13 | ;; We can't directly alter CS, so we far jump to change it 14 | jmp 0x08:.reload_CS ; 0x08 points at the new code selector (2nd in our GDT) 15 | .reload_CS: 16 | ;; Reload data segment registers: 17 | mov ax, 0x10 ; 0x10 points at the new data selector (3rd in our GDT) 18 | mov ds, ax 19 | mov es, ax 20 | mov fs, ax 21 | mov gs, ax 22 | mov ss, ax 23 | ;; return from loadGDT 24 | ret 25 | 26 | ;;; loadIDT: func (IDTDescriptor*) 27 | global loadIDT 28 | loadIDT: 29 | push eax 30 | mov eax, [esp+0x8] ; get the struct pointer 31 | lidt [eax] ; load the IDT 32 | pop eax 33 | ret ; return 34 | 35 | ;;; enableInterrupts: func 36 | global enableInterrupts 37 | enableInterrupts: 38 | sti 39 | ret 40 | 41 | ;;; disableInterrupts: func 42 | global disableInterrupts 43 | disableInterrupts: 44 | cli 45 | ret 46 | 47 | ;;; halt: func 48 | global halt 49 | halt: 50 | hlt 51 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export OOC_LIBS = . 2 | OOC := rock 3 | CC := gcc 4 | QEMU := qemu-system-i386 5 | 6 | CCFLAGS := +-m32 +-nostdinc +-ffreestanding +-fno-stack-protector 7 | OOCFLAGS := -c -v -g --gc=off --nomain -Iinclude $(CCFLAGS) 8 | 9 | LDFLAGS := -melf_i386 -nostdlib -g 10 | ASFLAGS := -felf32 -g 11 | 12 | STAGE2 := /boot/grub/stage2_eltorito 13 | 14 | OOCFILES := $(shell find "src" -name "*.ooc") 15 | ASMFILES := $(shell find "src" -name "*.asm") 16 | ASMOBJFILES := $(patsubst %.asm,%.o,$(ASMFILES)) 17 | 18 | .PHONY: all clean bochs bochs-dbg qemu 19 | 20 | all: oos.iso 21 | 22 | bochs: oos.iso 23 | @bochs -qf .bochsrc 24 | 25 | bochs-dbg: oos.iso 26 | @bochs -qf .bochsrc-dbg 27 | 28 | qemu: oos.iso 29 | @$(QEMU) -cdrom $< -net none 30 | 31 | oos.iso: oos.exe isofs/boot/grub/stage2_eltorito isofs/boot/grub/menu.lst 32 | @mkdir -p isofs/system 33 | cp $< isofs/system 34 | genisoimage -R -b boot/grub/stage2_eltorito -no-emul-boot -boot-load-size 4 -boot-info-table -input-charset utf-8 -o $@ isofs 35 | 36 | oos.exe: ${ASMOBJFILES} src/oos.lib 37 | ${LD} ${LDFLAGS} -T src/linker.ld -o $@ ${ASMOBJFILES} src/oos.lib 38 | 39 | src/oos.lib: ${OOCFILES} 40 | ${OOC} ${OOCFLAGS} --entrypoint=kmain oos.use 41 | 42 | %.o: %.asm 43 | nasm ${ASFLAGS} -o $@ $< 44 | 45 | isofs/boot/grub/stage2_eltorito: 46 | mkdir -p isofs/boot/grub 47 | cp ${STAGE2} $@ 48 | 49 | clean: 50 | $(RM) -r $(wildcard $(ASMOBJFILES) src/oos.lib oos.exe oos.iso rock_tmp ooc_tmp build .libs) 51 | -------------------------------------------------------------------------------- /src/Bochs.ooc: -------------------------------------------------------------------------------- 1 | import devices/Ports 2 | 3 | Bochs: cover { 4 | // Bochs uses this port to let us print to the bochs console. 5 | PORT_E9 := static 0xE9 6 | 7 | // Read http://bochs.sourceforge.net/doc/docbook/development/iodebug.html 8 | // iodebug command port. 9 | IODEBUG_COMMAND := static 0x8A00 10 | // iodebug enable command. 11 | IODEBUG_ENABLE := static 0x8A00 12 | // iodebug breakpoint command. 13 | IODEBUG_BREAKPOINT := static 0x8AE0 14 | 15 | // This function drops to the debugger in bochs. It only works if 16 | // you compile bochs with --enable-iodebug and --enable-debugger, 17 | // which is incompatible with --enable-gdb-stub. 18 | breakpoint: static func { 19 | // Enable the bochs iodebug module. 20 | Ports outWord(IODEBUG_COMMAND, IODEBUG_ENABLE) 21 | // Drop to debugger prompt. 22 | Ports outWord(IODEBUG_COMMAND, IODEBUG_BREAKPOINT) 23 | } 24 | 25 | print: static func ~char (chr: Char) { 26 | Ports outByte(PORT_E9, chr as UInt8) 27 | } 28 | 29 | print: static func ~string (str: String) { 30 | for(i in 0..str length()) { 31 | print(str[i]) 32 | } 33 | } 34 | 35 | println: static func (str: String) { 36 | print(str) 37 | print('\n') 38 | } 39 | 40 | debug: static func (str: String) { 41 | print("[DEBUG] ") 42 | println(str) 43 | } 44 | 45 | warn: static func (str: String) { 46 | print("[WARNING] ") 47 | println(str) 48 | } 49 | 50 | error: static func (str: String) { 51 | print("[ERROR] ") 52 | println(str) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Console.ooc: -------------------------------------------------------------------------------- 1 | import devices/[Keyboard, Display], memory/MM 2 | 3 | Console: cover { 4 | run: static func { 5 | buffer: Char[50] 6 | bufferIndex := 0 7 | ">> " print() 8 | while(true) { 9 | chr := Keyboard read() 10 | if(chr == '\n') { 11 | '\n' print() 12 | cmd := String new(50) 13 | for(i in 0..bufferIndex) 14 | cmd[i] = buffer[i] 15 | cmd[bufferIndex] = '\0' 16 | handleCommand(cmd) 17 | bufferIndex = 0 18 | ">> " print() 19 | } else if(chr == '\b') { 20 | if(bufferIndex > 0) { 21 | bufferIndex -= 1 22 | "\b \b" print() 23 | } 24 | } else if(bufferIndex < 50) { 25 | chr print() 26 | buffer[bufferIndex] = chr 27 | bufferIndex += 1 28 | } 29 | } 30 | } 31 | 32 | handleCommand: static func (cmd: String) { 33 | match cmd { 34 | case "memory" => 35 | "Total Memory: %6i kB" printfln(MM memorySize / 1024) 36 | "Used Memory: %6i kB" printfln(MM usedMemory / 1024) 37 | "Free Memory: %6i kB" printfln(MM freeMemory / 1024) 38 | } 39 | } 40 | } 41 | 42 | operator == (lhs, rhs: String) -> Bool { 43 | if ((lhs == null) || (rhs == null)) { 44 | return false 45 | } 46 | 47 | rhslen := rhs length() 48 | if (lhs length() != rhslen) { 49 | return false 50 | } 51 | 52 | s1 := lhs as Char* 53 | s2 := rhs as Char* 54 | for (i in 0..rhslen) { 55 | if (s1[i] != s2[i]) { 56 | return false 57 | } 58 | } 59 | return true 60 | } 61 | -------------------------------------------------------------------------------- /src/devices/cpu/IDT.ooc: -------------------------------------------------------------------------------- 1 | include IDT 2 | 3 | // These covers wouldn't have to be from C if we could do GCC's 4 | // __attribute__((packed)) from ooc somehow 5 | IDTDescriptor: cover from IDTD { 6 | size: extern UInt16 7 | offset: extern UInt32 8 | } // __attribute__((packed)) 9 | 10 | IDTGate: cover from IDTG { 11 | offset_1: extern UInt16 // offset bits 0..15 12 | selector: extern UInt16 // code segment selector in GDT 13 | zero: extern UInt8 // unused, set to 0 14 | type_attr: extern UInt8 // type and attributes 15 | offset_2: extern UInt16 // offset bits 16..31 16 | } // __attribute__((packed)) 17 | 18 | IDT: cover { 19 | TASK := static 0x5 20 | INTR16 := static 0x6 21 | INTR32 := static 0xe 22 | TRAP16 := static 0x7 23 | TRAP32 := static 0xf 24 | 25 | // idt: static IDTGate[256] 26 | idt: static IDTGate* 27 | 28 | // defined in IDT.asm 29 | load: extern(loadIDT) proto static func (IDTDescriptor*) 30 | 31 | setup: static func { 32 | idt = gc_malloc(IDTGate size * 256) 33 | 34 | idtd: IDTDescriptor 35 | idtd offset = idt as UInt32 36 | idtd size = IDTGate size * 256 - 1 37 | 38 | zeroMemory(idt, idtd size) 39 | 40 | load(idtd&) 41 | } 42 | 43 | setGate: static func (n: SizeT, offset: Pointer, selector: UInt16, priv, sys, gatetype: UInt8) { 44 | idt[n] offset_1 = offset as UInt32 & 0xffff // offset bits 0..15 45 | idt[n] offset_2 = offset as UInt32 >> 16 & 0xffff // offset bits 16..31 46 | 47 | idt[n] selector = selector 48 | idt[n] zero = 0 // unused 49 | 50 | idt[n] type_attr = (1 << 7) | // first bit must be set for all valid descriptors 51 | ((priv & 0b11) << 5) | // two bits for the ring level 52 | ((sys & 0b1) << 4) | // one bit for system segment 53 | (gatetype & 0b1111) // four bits for gate type 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Multiboot.ooc: -------------------------------------------------------------------------------- 1 | // The magic number passed by a Multiboot-compliant boot loader 2 | MULTIBOOT_BOOTLOADER_MAGIC := 0x2BADB002 3 | 4 | // Multiboot Flags 5 | MULTIBOOT_FLAG_MEM := 1 << 0 6 | MULTIBOOT_FLAG_DEVICE := 1 << 1 7 | MULTIBOOT_FLAG_CMDLINE := 1 << 2 8 | MULTIBOOT_FLAG_MODS := 1 << 3 9 | MULTIBOOT_FLAG_AOUT := 1 << 4 10 | MULTIBOOT_FLAG_ELF := 1 << 5 11 | MULTIBOOT_FLAG_MMAP := 1 << 6 12 | MULTIBOOT_FLAG_CONFIG := 1 << 7 13 | MULTIBOOT_FLAG_LOADER := 1 << 8 14 | MULTIBOOT_FLAG_APM := 1 << 9 15 | MULTIBOOT_FLAG_VBE := 1 << 10 16 | 17 | // Global variable for multiboot info so it can be accessed everywhere. 18 | multiboot: MultibootInfo 19 | 20 | // Structure containing information received from GRUB (or any 21 | // other Multiboot-compliant bootloader). 22 | MultibootInfo: cover { 23 | flags, // Determines which fields below are present. 24 | 25 | memLower, // Amount of lower memory in the computer (in kB). 26 | memUpper, // Amount of higher memory in the computer (in kB). 27 | 28 | bootDevice, // The boot device which is used to boot the kernel. 29 | 30 | cmdline, // The command line passed to the OS by GRUB. Example: /System/oos.exe 31 | 32 | modsCount, // Amount of modules loaded (e.g. ramdisks). 33 | modsAddr, // Address of the modules. 34 | 35 | // ELF stuff. 36 | elfNumber, 37 | elfSize, 38 | elfAddress, 39 | elfShndx, 40 | 41 | mmapLength, // Memory map length. 42 | mmapAddr, // Memory map starting address. 43 | 44 | // Example: root (fd0) 45 | drivesLength, // Amount of drives available. 46 | drivesAddr, // Starting address of the drives. 47 | 48 | configTable, 49 | 50 | bootLoaderName, // Name of the bootloader. Example: GNU GRUB 0.97 51 | 52 | apmTable, // APM (Advanced Power Management) table. 53 | 54 | vbeControlInfo, 55 | vbeModeInfo, 56 | vbeMode, 57 | vbeInterfaceSegment, 58 | vbeInterfaceOffset, 59 | vbeInterfaceLength: UInt32 60 | } 61 | 62 | // The module structure. 63 | MultibootModule: cover { 64 | moduleStart, 65 | moduleEnd, 66 | string, 67 | reserved: UInt32 68 | } 69 | 70 | // Memory map entry. 71 | MMapEntry: cover { 72 | size, // Size of the structure (not counting this variable). 73 | baseAddrLow, // Memory region starting address. 74 | baseAddrHigh, // Upper 32-bits of the previous value (for 64-bits systems). 75 | lengthLow, // Memory region length. 76 | lengthHigh, // Upper 32-bits of the previous value (for 64-bits systems). 77 | type: UInt32 // Type (1 = available RAM, 0 = reserved). 78 | } 79 | -------------------------------------------------------------------------------- /src/devices/cpu/interrupts.asm: -------------------------------------------------------------------------------- 1 | bits 32 2 | 3 | section .text 4 | 5 | ;;; 6 | ;;; Exceptions, ISRs, IRQs, Syscalls 7 | ;;; 8 | 9 | extern isrHandler 10 | extern irqHandler 11 | 12 | global isrSyscall 13 | isrSyscall: 14 | cli 15 | push 0 16 | push 0x80 17 | jmp isrCommon 18 | iret 19 | 20 | %macro HANDLER_COMMON 1 21 | %1Common: 22 | pusha 23 | push ds 24 | push es 25 | push fs 26 | push gs 27 | mov ax, 0x10 28 | mov ds, ax 29 | mov es, ax 30 | mov fs, ax 31 | mov gs, ax 32 | mov eax, esp 33 | push eax 34 | mov eax, %1Handler 35 | call eax 36 | pop eax 37 | pop gs 38 | pop fs 39 | pop es 40 | pop ds 41 | popa 42 | add esp, 8 43 | iret 44 | %endmacro 45 | 46 | ;;; ISRs 47 | 48 | %macro ISR_COMMON 1 49 | global isr%1 50 | isr%1: 51 | cli 52 | push byte 0 53 | push byte %1 54 | jmp isrCommon 55 | iret 56 | %endmacro 57 | 58 | %macro ISR_ABORT 1 59 | ISR_COMMON %1 60 | %endmacro 61 | 62 | %macro ISR_FAULT 1 63 | ISR_COMMON %1 64 | %endmacro 65 | 66 | %macro ISR_INTR 1 67 | ISR_COMMON %1 68 | %endmacro 69 | 70 | %macro ISR_RESV 1 71 | ISR_COMMON %1 72 | %endmacro 73 | 74 | %macro ISR_TRAP 1 75 | ISR_COMMON %1 76 | %endmacro 77 | 78 | section .text 79 | 80 | ISR_FAULT 0 81 | ISR_FAULT 1 82 | ISR_INTR 2 83 | ISR_TRAP 3 84 | ISR_TRAP 4 85 | ISR_FAULT 5 86 | ISR_FAULT 6 87 | ISR_FAULT 7 88 | ISR_ABORT 8 89 | ISR_FAULT 9 90 | ISR_FAULT 10 91 | ISR_FAULT 11 92 | ISR_FAULT 12 93 | ISR_FAULT 13 94 | ISR_FAULT 14 95 | ISR_FAULT 15 96 | ISR_FAULT 16 97 | ISR_FAULT 17 98 | ISR_ABORT 18 99 | ISR_FAULT 19 100 | ISR_RESV 20 101 | ISR_RESV 21 102 | ISR_RESV 22 103 | ISR_RESV 23 104 | ISR_RESV 24 105 | ISR_RESV 25 106 | ISR_RESV 26 107 | ISR_RESV 27 108 | ISR_RESV 28 109 | ISR_RESV 29 110 | ISR_RESV 30 111 | ISR_RESV 31 112 | 113 | HANDLER_COMMON isr 114 | 115 | ;;; IRQs 116 | 117 | %macro IRQ_COMMON 2 118 | global irq%1 119 | irq%1: 120 | cli 121 | push byte 0 122 | push byte %2 123 | jmp irqCommon 124 | iret 125 | %endmacro 126 | 127 | IRQ_COMMON 0, 32 128 | IRQ_COMMON 1, 33 129 | IRQ_COMMON 2, 34 130 | IRQ_COMMON 3, 35 131 | IRQ_COMMON 4, 36 132 | IRQ_COMMON 5, 37 133 | IRQ_COMMON 6, 38 134 | IRQ_COMMON 7, 39 135 | IRQ_COMMON 8, 40 136 | IRQ_COMMON 9, 41 137 | IRQ_COMMON 10, 42 138 | IRQ_COMMON 11, 43 139 | IRQ_COMMON 12, 44 140 | IRQ_COMMON 13, 45 141 | IRQ_COMMON 14, 46 142 | IRQ_COMMON 15, 47 143 | 144 | HANDLER_COMMON irq 145 | -------------------------------------------------------------------------------- /src/devices/cpu/GDT.ooc: -------------------------------------------------------------------------------- 1 | include GDT 2 | 3 | // See http://wiki.osdev.org/GDT, or none of this will make any sense 4 | 5 | GDT: cover { 6 | gdt: static GDTEntry[3] 7 | 8 | // defined in GDT.asm 9 | load: extern(loadGDT) proto static func (GDTDescriptor*) 10 | 11 | setup: static func { 12 | gdtd: GDTDescriptor 13 | gdtd offset = gdt as UInt32 14 | gdtd size = GDTEntry size * 3 - 1 15 | 16 | zeroMemory(gdt as Pointer, gdtd size) 17 | 18 | // Leave one null GDT entry at 0 because the CPU wants it 19 | setEntry(1, 0x00000000, 0xFFFFF, 0b00, true, false, true, true, true) 20 | setEntry(2, 0x00000000, 0xFFFFF, 0b00, false, false, true, true, true) 21 | 22 | load(gdtd&) 23 | } 24 | 25 | setEntry: static func (n: SizeT, base, limit: UInt32, privl: UInt, ex, dc, rw, gr, sz: Bool) { 26 | gdt[n] base_1 = base & 0xFFFF // base bits 0..15 27 | gdt[n] base_2 = base >> 16 & 0xFF // base bits 16..23 28 | gdt[n] base_3 = base >> 24 & 0xFF // base bits 24..31 29 | 30 | gdt[n] limit_1 = limit & 0xFFFF // limit bits 0..15 31 | gdt[n] flags__limit_2 = (((gr & 1) << 7) | // granularity bit 32 | ((sz & 1) << 6) | // size bit 33 | // ends with two dummy bits (00) 34 | (limit >> 16 & 0xF)) as UInt8 // limit bits 16..19 35 | 36 | gdt[n] access_byte = (1 << 7) | // first bit must be set for all valid GDT selectors 37 | ((privl & 0b11) << 5) | // two bits for the ring level 38 | (1 << 4) | // dummy bit, must be 1 39 | ((ex & 1) << 3) | // executable bit 40 | ((dc & 1) << 2) | // Direction bit/Conforming bit 41 | ((rw & 1) << 1) // Readable bit/Writable bit 42 | // Accessed bit (left as 0) 43 | } 44 | } 45 | 46 | // These covers wouldn't have to be from C if we could do GCC's 47 | // __attribute__((packed)) from ooc somehow 48 | GDTDescriptor: cover from GDTD { 49 | size: extern UInt16 50 | offset: extern UInt32 51 | } // __attribute__((packed)) 52 | 53 | GDTEntry: cover from GDTE { 54 | limit_1: extern UInt16 // limit bits 0..15 55 | base_1: extern UInt16 // base bits 0..15 56 | base_2: extern UInt8 // base bits 16..23 57 | access_byte: extern UInt8 // access: Pr, Privl (2), 1, Ex, DC, RW, Ac 58 | flags__limit_2: extern UInt8 // flags: Gr, Sz, 0, 0 59 | // limit bits 16..19 60 | base_3: extern UInt8 // base bits 24..31 61 | } // __attribute__((packed)) 62 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | oos 2 | === 3 | 4 | * oos is an Operating System with the goal of using [the ooc language][ooc] for 5 | as much of the code as possible 6 | * oos stands for ooc operating system 7 | * Repository: [http://github.com/tsion/oos][repo] 8 | 9 | 10 | Downloading and Compiling oos 11 | ----------------------------- 12 | 13 | You will need to have installed gcc, make, nasm, genisoimage (cdrkit), 14 | and rock (the ooc compiler). I recommend you get the very 15 | latest version of rock from the [git repo][rockgit], because ooc is 16 | still in development and oos usually relies on some pretty recent 17 | bugfixes. If you want to run oos in an emulator, bochs is a good 18 | choice, and there is a makefile target for it. 19 | 20 | Also, if you don't have `/boot/grub/stage2_eltorito`, you should `wget 21 | http://osdev.googlecode.com/svn-history/r12/trunk/geekos/build/x86/boot/stage2_eltorito` 22 | and move it to `isofs/boot/grub/stage2_eltorito`. 23 | 24 | If you have `stage2_eltorito`, but it's just in a different location, 25 | run `make STAGE2=/path/to/stage2_eltorito`. 26 | 27 | After you have all that, it is as simple as: 28 | 29 | $ git clone git://github.com/tsion/oos.git 30 | $ cd oos 31 | $ make bochs 32 | 33 | You can leave off 'bochs' if you only want to build the ISO. 34 | 35 | 36 | Running oos 37 | ----------- 38 | 39 | As seen above, I've provided a makefile target for running oos in the 40 | Bochs emulator. You should be able to use oos with qemu or any 41 | emulator of your choice by telling your emulator to boot the oos.iso 42 | CD image. 43 | 44 | $ qemu -cdrom oos.iso 45 | 46 | Running oos on real hardware is untested and not recommended (I am not 47 | responsible for any fires, explosions, or alien abductions that may 48 | result), but in theory it should work. I'll keep my fingers crossed. 49 | 50 | UPDATE: [sdkmvx][sdkmvx] has tested oos on real hardware, and it worked for him! 51 | 52 | 53 | Debugging oos 54 | ------------- 55 | 56 | I have also created a make target to run bochs listening for a remote 57 | gdb connection on port 1234. You need to compile bochs with 58 | --enable-gdb-stub to use this script! 59 | 60 | $ make bochs-dbg 61 | 62 | When you run that, bochs will say "Waiting for gdb connection on port 63 | 1234." You can now connect with gdb from another terminal. Use 64 | --symbols=oos.exe so that gdb is aware of our function and variable 65 | names. 66 | 67 | $ gdb --symbols=oos.exe 68 | GNU gdb (GDB) 7.0 69 | Copyright (C) 2009 Free Software Foundation, Inc. 70 | License GPLv3+: GNU GPL version 3 or later 71 | This is free software: you are free to change and redistribute it. 72 | There is NO WARRANTY, to the extent permitted by law. Type "show copying" 73 | and "show warranty" for details. 74 | This GDB was configured as "x86_64-unknown-linux-gnu". 75 | For bug reporting instructions, please see: 76 | . 77 | Reading symbols from /home/scott/code/os/oos/Src/Kernel/oos.exe...done. 78 | (gdb) target remote :1234 79 | Remote debugging using :1234 80 | warning: Remote failure reply: Eff 81 | 0x0000fff0 in ?? () 82 | (gdb) break kmain 83 | Breakpoint 1 at 0x100c2c: file ooc_tmp/oos/Src/Kernel/Main.c, line 11. 84 | (gdb) c 85 | Continuing. 86 | 87 | Breakpoint 1, kmain (mb=0x354a0, magic=732803074) at ooc_tmp/oos/Src/Kernel/Main.c:11 88 | 11 void kmain(MultibootInfo *mb, uint32_t magic) { 89 | (gdb) bt 90 | #0 kmain (mb=0x354a0, magic=732803074) at ooc_tmp/oos/Src/Kernel/Main.c:11 91 | #1 0x00100018 in _start () at Src/Kernel/Boot.asm:25 92 | 93 | This can be extremely useful for finding those annoying little 94 | problems! The same can also be done with any GUI front-end to gdb, just 95 | consult its documentation on how to do a remote gdb connection. And by 96 | the way, since I compile with ooc -g, gdb can walk through ooc code 97 | line by line, and display the ooc source of the line it's on. Hooray! 98 | 99 | Note: gdb can do a *lot*, check out `help`. 100 | 101 | Note again: [nemiver][nemiver] is a decent GTK+ GUI front-end for gdb. Or 102 | if you like to like to live on the 'K' side of life, there is [kdbg][kdbg]. 103 | 104 | 105 | Thanks 106 | ------ 107 | 108 | * [nddrylliog][ndd] and everyone else involved in creating [the ooc language][ooc]! 109 | * [OSDev][osdev], for all the great tutorials and informative articles 110 | * The creators of [dux OS][dux]. I read and stole a lot of code from them. :) 111 | 112 | 113 | [repo]: http://github.com/tsion/oos 114 | [ooc]: http://ooc-lang.org 115 | [rockgit]: http://github.com/nddrylliog/rock 116 | [sdkmvx]: http://github.com/martinbrandenburg 117 | [nemiver]: http://projects.gnome.org/nemiver/ 118 | [kdbg]: http://www.kdbg.org/ 119 | [ndd]: http://github.com/nddrylliog 120 | [osdev]: http://wiki.osdev.org/Main_Page 121 | [dux]: http://github.com/RockerMONO/dux 122 | -------------------------------------------------------------------------------- /src/devices/Display.ooc: -------------------------------------------------------------------------------- 1 | import Ports 2 | 3 | print: func (str: String) { 4 | Display printString(str) 5 | } 6 | 7 | println: func (str: String) { 8 | Display printString(str) 9 | Display printChar('\n') 10 | } 11 | 12 | Color: enum { 13 | black = 0 14 | blue 15 | green 16 | cyan 17 | red 18 | magenta 19 | brown 20 | lightGrey 21 | darkGrey 22 | lightBlue 23 | lightGreen 24 | lightCyan 25 | lightRed 26 | lightMagenta 27 | yellow 28 | white 29 | } 30 | 31 | Display: cover { 32 | VIDEO_MEMORY := static 0xb8000 as UInt16* 33 | 34 | INDEX_PORT := static 0x3d4 35 | DATA_PORT := static 0x3d5 36 | 37 | CURSOR_LOW_PORT := static 0xE 38 | CURSOR_HIGH_PORT := static 0xF 39 | 40 | CONSOLE_WIDTH := static 80 41 | CONSOLE_HEIGHT := static 25 42 | 43 | color: static UInt8 44 | fg: static Color 45 | bg: static Color 46 | cursor_x: static Int 47 | cursor_y: static Int 48 | 49 | setup: static func { 50 | // Default to light grey on black like the BIOS. 51 | setColor(Color lightGrey, Color black) 52 | clearScreen() 53 | } 54 | 55 | setColor: static func (fg, bg: Color) { 56 | This fg = fg 57 | This bg = bg 58 | This color = ((fg & 0xf) | bg << 4) as UInt8 59 | } 60 | 61 | setColor: static func ~withFn (fg, bg: Color, fn: Func) { 62 | oldFg := This fg 63 | oldBg := This bg 64 | setColor(fg, bg) 65 | fn() 66 | setColor(oldFg, oldBg) 67 | } 68 | 69 | setFgColor: static func (fg: Color) { 70 | setColor(fg, This bg) 71 | } 72 | 73 | setFgColor: static func ~withFn (fg: Color, fn: Func) { 74 | oldFg := This fg 75 | setFgColor(fg) 76 | fn() 77 | setFgColor(oldFg) 78 | } 79 | 80 | setBgColor: static func (bg: Color) { 81 | setColor(This fg, bg) 82 | } 83 | 84 | setBgColor: static func ~withFn (bg: Color, fn: Func) { 85 | oldBg := This bg 86 | setBgColor(bg) 87 | fn() 88 | setBgColor(oldBg) 89 | } 90 | 91 | clearScreen: static func { 92 | for(i in 0..(CONSOLE_HEIGHT * CONSOLE_WIDTH)) { 93 | VIDEO_MEMORY[i] = (' ' | color << 8) as UInt16 94 | } 95 | cursor_x = 0 96 | cursor_y = 0 97 | updateCursor() 98 | } 99 | 100 | updateCursor: static func { 101 | position := cursor_y * CONSOLE_WIDTH + cursor_x 102 | 103 | Ports outByte(INDEX_PORT, CURSOR_LOW_PORT) 104 | Ports outByte(DATA_PORT, position >> 8) 105 | 106 | Ports outByte(INDEX_PORT, CURSOR_HIGH_PORT) 107 | Ports outByte(DATA_PORT, position) 108 | } 109 | 110 | printChar: static func (chr: Char) { 111 | // Handle a backspace, by moving the cursor back one space 112 | if(chr == '\b') { 113 | if (cursor_x != 0) cursor_x -= 1 114 | } 115 | 116 | // Handles a tab by incrementing the cursor's x, but only 117 | // to a point that will make it divisible by 8 118 | else if(chr == '\t') { 119 | cursor_x = cursor_x align(8) 120 | } 121 | 122 | // Handles a 'Carriage Return', which simply brings the 123 | // cursor back to the margin 124 | else if(chr == '\r') { 125 | cursor_x = 0 126 | } 127 | 128 | // We handle our newlines the way DOS and the BIOS do: we 129 | // treat it as if a 'CR' was also there, so we bring the 130 | // cursor to the margin and we increment the 'y' value 131 | else if(chr == '\n') { 132 | cursor_x = 0 133 | cursor_y += 1 134 | } 135 | 136 | // Any character greater than and including a space, is a 137 | // printable character. The equation for finding the index 138 | // in a linear chunk of memory can be represented by: 139 | // Index = [(y * width) + x] 140 | else if(chr >= ' ') { 141 | i := cursor_y * CONSOLE_WIDTH + cursor_x 142 | VIDEO_MEMORY[i] = (chr | color << 8) as UInt16 143 | cursor_x += 1 144 | } 145 | 146 | // If the cursor has reached the edge of the screen's width, we 147 | // insert a new line in there 148 | if(cursor_x >= CONSOLE_WIDTH) { 149 | cursor_x = 0 150 | cursor_y += 1 151 | } 152 | 153 | // If the cursor has gone below the bottom of the screen, we 154 | // scroll the screen 155 | if(cursor_y >= CONSOLE_HEIGHT) { 156 | cursor_y -= 1 157 | scroll() 158 | } 159 | 160 | updateCursor() 161 | } 162 | 163 | scroll: static func { 164 | for(row in 1..CONSOLE_HEIGHT) { 165 | for(col in 0..CONSOLE_WIDTH) { 166 | VIDEO_MEMORY[(row - 1) * CONSOLE_WIDTH + col] = VIDEO_MEMORY[row * CONSOLE_WIDTH + col] 167 | } 168 | } 169 | 170 | for(col in 0..CONSOLE_WIDTH) { 171 | VIDEO_MEMORY[(CONSOLE_HEIGHT - 1) * CONSOLE_WIDTH + col] = (' ' | color << 8) as UInt16 172 | } 173 | } 174 | 175 | printString: static func (str: String) { 176 | for(i in 0..str length()) { 177 | printChar(str[i]) 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/devices/cpu/ISR.ooc: -------------------------------------------------------------------------------- 1 | import IDT, SysCall, Registers, Panic 2 | 3 | exceptionMessages: const String* // [32] 4 | 5 | ISR: cover { 6 | // from exceptions.asm 7 | isr0: extern proto static func 8 | isr1: extern proto static func 9 | isr2: extern proto static func 10 | isr3: extern proto static func 11 | isr4: extern proto static func 12 | isr5: extern proto static func 13 | isr6: extern proto static func 14 | isr7: extern proto static func 15 | isr8: extern proto static func 16 | isr9: extern proto static func 17 | isr10: extern proto static func 18 | isr11: extern proto static func 19 | isr12: extern proto static func 20 | isr13: extern proto static func 21 | isr14: extern proto static func 22 | isr15: extern proto static func 23 | isr16: extern proto static func 24 | isr17: extern proto static func 25 | isr18: extern proto static func 26 | isr19: extern proto static func 27 | isr20: extern proto static func 28 | isr21: extern proto static func 29 | isr22: extern proto static func 30 | isr23: extern proto static func 31 | isr24: extern proto static func 32 | isr25: extern proto static func 33 | isr26: extern proto static func 34 | isr27: extern proto static func 35 | isr28: extern proto static func 36 | isr29: extern proto static func 37 | isr30: extern proto static func 38 | isr31: extern proto static func 39 | 40 | handler: unmangled(isrHandler) static func (regs: Registers@) { 41 | if(regs interruptNumber == 0x80) { 42 | SysCall handler(regs&) 43 | } else if(regs interruptNumber == 3) { 44 | // Handle breakpoints here 45 | panic(exceptionMessages[3]) 46 | } else if(regs interruptNumber < 32) { 47 | // panic with appropriate message 48 | panic(exceptionMessages[regs interruptNumber]) 49 | } 50 | } 51 | 52 | setup: static func { 53 | exceptionMessages = gc_malloc(String size * 32) 54 | exceptionMessages[0] = "0 #DE Divide Error" 55 | exceptionMessages[1] = "1 #DB RESERVED" 56 | exceptionMessages[2] = "2 - NMI Interrupt" 57 | exceptionMessages[3] = "3 #BP Breakpoint" 58 | exceptionMessages[4] = "4 #OF Overflow" 59 | exceptionMessages[5] = "5 #BR BOUND Range Exceeded" 60 | exceptionMessages[6] = "6 #UD Invalid Opcode (Undefined Opcode)" 61 | exceptionMessages[7] = "7 #NM Device Not Available (No Math Coprocessor)" 62 | exceptionMessages[8] = "8 #DF Double Fault" 63 | exceptionMessages[9] = "9 Coprocessor Segment Overrun (reserved)" 64 | exceptionMessages[10] = "10 #TS Invalid TSS" 65 | exceptionMessages[11] = "11 #NP Segment Not Present" 66 | exceptionMessages[12] = "12 #SS Stack-Segment Fault" 67 | exceptionMessages[13] = "13 #GP General Protection" 68 | exceptionMessages[14] = "14 #PF Page Fault" 69 | exceptionMessages[15] = "15 - (Intel reserved. Do not use.)" 70 | exceptionMessages[16] = "16 #MF x87 FPU Floating-Point Error (Math Fault)" 71 | exceptionMessages[17] = "17 #AC Alignment Check" 72 | exceptionMessages[18] = "18 #MC Machine Check" 73 | exceptionMessages[19] = "19 #XM SIMD Floating-Point Exception" 74 | exceptionMessages[20] = "20 - Intel reserved. Do not use." 75 | exceptionMessages[21] = "21 - Intel reserved. Do not use." 76 | exceptionMessages[22] = "22 - Intel reserved. Do not use." 77 | exceptionMessages[23] = "23 - Intel reserved. Do not use." 78 | exceptionMessages[24] = "24 - Intel reserved. Do not use." 79 | exceptionMessages[25] = "25 - Intel reserved. Do not use." 80 | exceptionMessages[26] = "26 - Intel reserved. Do not use." 81 | exceptionMessages[27] = "27 - Intel reserved. Do not use." 82 | exceptionMessages[28] = "28 - Intel reserved. Do not use." 83 | exceptionMessages[29] = "29 - Intel reserved. Do not use." 84 | exceptionMessages[30] = "30 - Intel reserved. Do not use." 85 | exceptionMessages[31] = "31 - Intel reserved. Do not use." 86 | 87 | IDT setGate(0, isr0, 0x8, 0, 0, IDT INTR32) 88 | IDT setGate(1, isr1, 0x8, 0, 0, IDT INTR32) 89 | IDT setGate(2, isr2, 0x8, 0, 0, IDT INTR32) 90 | IDT setGate(3, isr3, 0x8, 0, 0, IDT INTR32) 91 | IDT setGate(4, isr4, 0x8, 0, 0, IDT INTR32) 92 | IDT setGate(5, isr5, 0x8, 0, 0, IDT INTR32) 93 | IDT setGate(6, isr6, 0x8, 0, 0, IDT INTR32) 94 | IDT setGate(7, isr7, 0x8, 0, 0, IDT INTR32) 95 | IDT setGate(8, isr8, 0x8, 0, 0, IDT INTR32) 96 | IDT setGate(9, isr9, 0x8, 0, 0, IDT INTR32) 97 | IDT setGate(10, isr10, 0x8, 0, 0, IDT INTR32) 98 | IDT setGate(11, isr11, 0x8, 0, 0, IDT INTR32) 99 | IDT setGate(12, isr12, 0x8, 0, 0, IDT INTR32) 100 | IDT setGate(13, isr13, 0x8, 0, 0, IDT INTR32) 101 | IDT setGate(14, isr14, 0x8, 0, 0, IDT INTR32) 102 | IDT setGate(15, isr15, 0x8, 0, 0, IDT INTR32) 103 | IDT setGate(16, isr16, 0x8, 0, 0, IDT INTR32) 104 | IDT setGate(17, isr17, 0x8, 0, 0, IDT INTR32) 105 | IDT setGate(18, isr18, 0x8, 0, 0, IDT INTR32) 106 | IDT setGate(19, isr19, 0x8, 0, 0, IDT INTR32) 107 | IDT setGate(20, isr20, 0x8, 0, 0, IDT INTR32) 108 | IDT setGate(21, isr21, 0x8, 0, 0, IDT INTR32) 109 | IDT setGate(22, isr22, 0x8, 0, 0, IDT INTR32) 110 | IDT setGate(23, isr23, 0x8, 0, 0, IDT INTR32) 111 | IDT setGate(24, isr24, 0x8, 0, 0, IDT INTR32) 112 | IDT setGate(25, isr25, 0x8, 0, 0, IDT INTR32) 113 | IDT setGate(26, isr26, 0x8, 0, 0, IDT INTR32) 114 | IDT setGate(27, isr27, 0x8, 0, 0, IDT INTR32) 115 | IDT setGate(28, isr28, 0x8, 0, 0, IDT INTR32) 116 | IDT setGate(29, isr29, 0x8, 0, 0, IDT INTR32) 117 | IDT setGate(30, isr30, 0x8, 0, 0, IDT INTR32) 118 | IDT setGate(31, isr31, 0x8, 0, 0, IDT INTR32) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/devices/cpu/IRQ.ooc: -------------------------------------------------------------------------------- 1 | /* Taken from : 2 | * 3 | *> An interrupt is a message from a device, such as the keyboard, to the CPU, 4 | *> telling it to immediately stop whatever it is currently doing and do 5 | *> something else. For example, the keyboard controller sends an interrupt when 6 | *> a key is pressed. To know what to do when a specifc interrupt arise, the CPU 7 | *> has a table called the IDT which is setup by the OS, and stored in memory. 8 | *> There are 256 interrupts, numbered from 0 to 255, but only 16 are used by 9 | *> devices. These are called IRQs (Interrupt ReQuest) or hardware interrupts. 10 | *> The 16 IRQs are numbered from 0 to 15. */ 11 | 12 | /* Imports: 13 | * 14 | * - IDT: So we can set gates for interrupts. 15 | * - Ports: So we can program the PICs. 16 | * - Registers: A struct given to interrupt handlers. */ 17 | import IDT, Registers, devices/Ports 18 | 19 | /* `IRQ` is only used as a namespace. */ 20 | IRQ: cover { 21 | /* An array storing pointers to the interrupt handler functions. */ 22 | irqRoutines: static Pointer* // [16] 23 | 24 | /* All of these `irq` functions are defined in Hal.asm. The `proto` keyword 25 | * is used because there is no C header that declares these functions. */ 26 | irq0: extern proto static func 27 | irq1: extern proto static func 28 | irq2: extern proto static func 29 | irq3: extern proto static func 30 | irq4: extern proto static func 31 | irq5: extern proto static func 32 | irq6: extern proto static func 33 | irq7: extern proto static func 34 | irq8: extern proto static func 35 | irq9: extern proto static func 36 | irq10: extern proto static func 37 | irq11: extern proto static func 38 | irq12: extern proto static func 39 | irq13: extern proto static func 40 | irq14: extern proto static func 41 | irq15: extern proto static func 42 | 43 | /* These are the Master PIC command and data port numbers. */ 44 | PIC1_COMMAND := static 0x20 45 | PIC1_DATA := static 0x21 46 | 47 | /* These are the Slave PIC command and data port numbers. */ 48 | PIC2_COMMAND := static 0xA0 49 | PIC2_DATA := static 0xA1 50 | 51 | /* This is a command that is sent to the PICs, telling them we are finished 52 | * handling the interrupt, and to resume regularily scheduled programming. */ 53 | PIC_EOI := static 0x20 54 | 55 | /* Install a custom function to handle a certain IRQ number. */ 56 | handlerInstall: static func (irq: Int, handler: Func (Registers*)) { 57 | irqRoutines[irq] = handler as Closure thunk 58 | } 59 | 60 | /* Uninstall the function that handles a certain IRQ number. */ 61 | handlerUninstall: static func (irq: Int) { 62 | irqRoutines[irq] = null 63 | } 64 | 65 | /* Normally, IRQs 0 to 7 are mapped to entries 8 to 15. This 66 | * is a problem in protected mode, because IDT entry 8 is a 67 | * Double Fault! Without remapping, every time irq0 fires, 68 | * you get a Double Fault Exception, which is NOT actually 69 | * what's happening. We send commands to the Programmable 70 | * Interrupt Controller (PICs - also called the 8259's) in 71 | * order to make irq0 to 15 be remapped to IDT entries 32 to 72 | * 47. */ 73 | remapPIC: static func { 74 | // Tell the PICs to wait for our 3 initialization bytes (we want to reinitialize). 75 | Ports outByte(PIC1_COMMAND, 0x11) 76 | Ports outByte(PIC2_COMMAND, 0x11) 77 | 78 | // Set master PIC offset to 0x20 ( = IRQ0 = 32). 79 | Ports outByte(PIC1_DATA, 0x20) 80 | // Set slave PIC offset to 0x28 ( = IRQ8 = 40). 81 | Ports outByte(PIC2_DATA, 0x28) 82 | 83 | // Set the wiring to 'attached to corresponding interrupt pin'. 84 | Ports outByte(PIC1_DATA, 0x04) 85 | Ports outByte(PIC2_DATA, 0x02) 86 | 87 | // We want to use 8086/8088 mode (bit 0). 88 | Ports outByte(PIC1_DATA, 0x01) 89 | Ports outByte(PIC2_DATA, 0x01) 90 | 91 | // Restore masking (if a bit is not set, the interrupts is on). 92 | Ports outByte(PIC1_DATA, 0x00) 93 | Ports outByte(PIC2_DATA, 0x00) 94 | } 95 | 96 | /* We first remap the interrupt controllers, and then we install 97 | * the appropriate ISRs to the correct entries in the IDT. This 98 | * is just like installing the exception handlers. */ 99 | setup: static func { 100 | irqRoutines = gc_malloc(Pointer size * 16) 101 | for(i in 0..16) 102 | irqRoutines[i] = null 103 | 104 | remapPIC() 105 | 106 | IDT setGate(32, irq0, 0x8, 0, 0, IDT INTR32) 107 | IDT setGate(33, irq1, 0x8, 0, 0, IDT INTR32) 108 | IDT setGate(34, irq2, 0x8, 0, 0, IDT INTR32) 109 | IDT setGate(35, irq3, 0x8, 0, 0, IDT INTR32) 110 | IDT setGate(36, irq4, 0x8, 0, 0, IDT INTR32) 111 | IDT setGate(37, irq5, 0x8, 0, 0, IDT INTR32) 112 | IDT setGate(38, irq6, 0x8, 0, 0, IDT INTR32) 113 | IDT setGate(39, irq7, 0x8, 0, 0, IDT INTR32) 114 | IDT setGate(40, irq8, 0x8, 0, 0, IDT INTR32) 115 | IDT setGate(41, irq9, 0x8, 0, 0, IDT INTR32) 116 | IDT setGate(42, irq10, 0x8, 0, 0, IDT INTR32) 117 | IDT setGate(43, irq11, 0x8, 0, 0, IDT INTR32) 118 | IDT setGate(44, irq12, 0x8, 0, 0, IDT INTR32) 119 | IDT setGate(45, irq13, 0x8, 0, 0, IDT INTR32) 120 | IDT setGate(46, irq14, 0x8, 0, 0, IDT INTR32) 121 | IDT setGate(47, irq15, 0x8, 0, 0, IDT INTR32) 122 | } 123 | 124 | /* Each of the IRQ ISRs point to this function. The IRQ Controllers 125 | * need to be told when you are done servicing them, so you need to 126 | * send them an "End of Interrupt" command (0x20). There are two 8259 127 | * chips: The first exists at 0x20, the second exists at 0xA0. If the 128 | * second controller (an IRQ from 8 to 15) gets an interrupt, you need 129 | * to acknowledge the interrupt at BOTH controllers, otherwise, you 130 | * only send an EOI command to the first controller. If you don't send 131 | * an EOI, you won't raise any more IRQs. */ 132 | handler: unmangled(irqHandler) static func (regs: Registers*) { 133 | // This is a blank function pointer 134 | fn: Func (Registers*) 135 | c := fn& as Closure* 136 | 137 | /* Find out if we have a custom handler to run for this 138 | * IRQ, and then run it. */ 139 | c@ thunk = irqRoutines[regs@ interruptNumber - 32] 140 | if(c@ thunk != null) 141 | fn(regs) 142 | 143 | /* We need to send an EOI to the interrupt controllers when we are 144 | * done. Only send EOI to slave controller if it's involved (irqs 9 and 145 | * up). */ 146 | if(regs@ interruptNumber > 8) 147 | Ports outByte(PIC2_COMMAND, PIC_EOI) 148 | Ports outByte(PIC1_COMMAND, PIC_EOI) 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/devices/Keyboard.ooc: -------------------------------------------------------------------------------- 1 | import devices/cpu/IRQ, devices/Ports, Bochs 2 | 3 | Scancode: cover { 4 | ESC := static 0x01 5 | ENTER := static 0x1c 6 | LCTRL := static 0x1d 7 | LSHIFT := static 0x2a 8 | RSHIFT := static 0x36 9 | LALT := static 0x38 10 | CAPSLOCK := static 0x3a 11 | NUMLOCK := static 0x45 12 | SCROLLLOCK := static 0x46 13 | } 14 | 15 | Keyboard: cover { 16 | lowercase: static SSizeT[128] = [ 17 | 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', /* 9 */ 18 | '9', '0', '-', '=', 0x08, /* Backspace */ 19 | '\t', /* Tab */ 20 | 'q', 'w', 'e', 'r', /* 19 */ 21 | 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', /* Enter key */ 22 | 0, /* 29 - Control */ 23 | 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', /* 39 */ 24 | '\'', '`', 0, /* Left shift */ 25 | '\\', 'z', 'x', 'c', 'v', 'b', 'n', /* 49 */ 26 | 'm', ',', '.', '/', 0, /* Right shift */ 27 | '*', 28 | 0, /* Alt */ 29 | ' ', /* Space bar */ 30 | 0, /* Caps lock */ 31 | 0, /* 59 - F1 key ... > */ 32 | 0, 0, 0, 0, 0, 0, 0, 0, 33 | 0, /* < ... F10 */ 34 | 0, /* 69 - Num lock*/ 35 | 0, /* Scroll Lock */ 36 | 0, /* Home key */ 37 | 0, /* Up Arrow */ 38 | 0, /* Page Up */ 39 | '-', 40 | 0, /* Left Arrow */ 41 | 0, 42 | 0, /* Right Arrow */ 43 | '+', 44 | 0, /* 79 - End key*/ 45 | 0, /* Down Arrow */ 46 | 0, /* Page Down */ 47 | 0, /* Insert Key */ 48 | 0, /* Delete Key */ 49 | 0, 0, 0, 50 | 0, /* F11 Key */ 51 | 0, /* F12 Key */ 52 | 0 /* All other keys are undefined */ 53 | ] 54 | 55 | uppercase: static SSizeT[128] = [ 56 | 0, 27, '!', '@', '#', '$', '%', '^', '&', '*', /* 9 */ 57 | '(', ')', '_', '+', 0x08, /* Backspace */ 58 | '\t', /* Tab */ 59 | 'Q', 'W', 'E', 'R', /* 19 */ 60 | 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', /* Enter key */ 61 | 0, /* 29 - Control */ 62 | 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', /* 39 */ 63 | '"', '~', 0, /* Left shift */ 64 | '|', 'Z', 'X', 'C', 'V', 'B', 'N', /* 49 */ 65 | 'M', '<', '>', '?', 0, /* Right shift */ 66 | '*', 67 | 0, /* Alt */ 68 | ' ', /* Space bar */ 69 | 0, /* Caps lock */ 70 | 0, /* 59 - F1 key ... > */ 71 | 0, 0, 0, 0, 0, 0, 0, 0, 72 | 0, /* < ... F10 */ 73 | 0, /* 69 - Num lock*/ 74 | 0, /* Scroll Lock */ 75 | 0, /* Home key */ 76 | 0, /* Up Arrow */ 77 | 0, /* Page Up */ 78 | '-', 79 | 0, /* Left Arrow */ 80 | 0, 81 | 0, /* Right Arrow */ 82 | '+', 83 | 0, /* 79 - End key*/ 84 | 0, /* Down Arrow */ 85 | 0, /* Page Down */ 86 | 0, /* Insert Key */ 87 | 0, /* Delete Key */ 88 | 0, 0, 0, 89 | 0, /* F11 Key */ 90 | 0, /* F12 Key */ 91 | 0 /* All other keys are undefined */ 92 | ] 93 | 94 | ESCAPE_CODE := static const 0xE0 95 | 96 | // true if this key is being held down 97 | shift := static false 98 | alt := static false 99 | ctrl := static false 100 | 101 | // true if this key is enabled 102 | capslock := static false 103 | numlock := static false 104 | scrolllock := static false 105 | 106 | // true if the previous scancode was an escape code 107 | escaped := static false 108 | 109 | // buffer to store the scancodes 110 | buffer: static UInt8[1024] 111 | bufferIndex := static 0 112 | 113 | flushBuffer: static func { 114 | while(Ports inByte(0x64) bitSet?(0)) { 115 | Ports inByte(0x60) 116 | } 117 | } 118 | 119 | updateLights: static func { 120 | status: UInt8 = 0 121 | 122 | if(scrolllock) 123 | status |= 1 124 | if(numlock) 125 | status |= 2 126 | if(capslock) 127 | status |= 4 128 | 129 | // Wait for the keyboard to process our previous input if the 130 | // input buffer is full. 131 | while(Ports inByte(0x64) bitSet?(1)) {} 132 | Ports outByte(0x60, 0xED) 133 | while(Ports inByte(0x64) bitSet?(1)) {} 134 | Ports outByte(0x60, status) 135 | } 136 | 137 | read: static func -> Char { 138 | // Wait for keyboard input. The keyboard interrupt handler 139 | // will increment bufferIndex. 140 | while(bufferIndex == 0){} 141 | 142 | bufferIndex -= 1 143 | scancode: UInt8 = buffer[bufferIndex] 144 | 145 | Bochs debug("Reading... CAPS:%i, SHIFT:%i" format(capslock, shift)) 146 | 147 | Bochs debug("uppercase:%p lowercase:%p scancode:%i" format(uppercase, lowercase, scancode)) 148 | 149 | Bochs debug("%s" format((uppercase[scancode] == lowercase[scancode]) toString())) 150 | 151 | if((shift && !capslock) || (capslock && !shift)) { 152 | uppercase[scancode] as Char 153 | } else { 154 | lowercase[scancode] as Char 155 | } 156 | } 157 | 158 | setup: static func { 159 | // The keyboard interrupt handler. 160 | IRQ handlerInstall(1, |regs| 161 | scancode := Ports inByte(0x60) 162 | Bochs debug("Scancode: %i" format(scancode)) 163 | 164 | if(scancode == ESCAPE_CODE) { 165 | escaped = true 166 | } else if(scancode bitSet?(7)) { 167 | // This scancode defines that a key was just released (key-up). 168 | match(scancode bitClear(7)) { 169 | // Shift key release 170 | case Scancode LSHIFT => 171 | shift = false 172 | 173 | case Scancode RSHIFT => 174 | shift = false 175 | } 176 | } else { 177 | match scancode { 178 | // Shift key press 179 | case Scancode LSHIFT => 180 | shift = true 181 | 182 | case Scancode RSHIFT => 183 | shift = true 184 | 185 | case Scancode CAPSLOCK => 186 | capslock = !capslock 187 | This updateLights() 188 | 189 | case Scancode NUMLOCK => 190 | numlock = !numlock 191 | This updateLights() 192 | 193 | case Scancode SCROLLLOCK => 194 | scrolllock = !scrolllock 195 | This updateLights() 196 | 197 | // Any other scan code 198 | case => 199 | buffer[bufferIndex] = scancode 200 | bufferIndex += 1 201 | } 202 | } 203 | ) 204 | numlock = true 205 | 206 | updateLights() 207 | flushBuffer() 208 | } 209 | } 210 | 211 | -------------------------------------------------------------------------------- /src/memory/MM.ooc: -------------------------------------------------------------------------------- 1 | import Kernel, Multiboot, Panic, Bochs 2 | import structs/Bitmap 3 | 4 | MM: cover { 5 | /// Memory region covered by one frame. (4kB) 6 | FRAME_SIZE := static 4096 7 | 8 | /// Amount of memory (in bytes) in the computer. 9 | memorySize: static SizeT 10 | 11 | /// Total amount of memory in use. 12 | usedMemory: static SizeT 13 | 14 | /// Amount of free memory (in bytes). 15 | freeMemory: static SizeT { 16 | get { 17 | if(usedMemory > memorySize) { 18 | Bochs warn("The amount of allocated memory is higher than the amount of available memory!") 19 | return 0 20 | } 21 | memorySize - usedMemory 22 | } 23 | } 24 | 25 | /** Bitmap frames (each frame is a 4 kB memory area). If a bit is 26 | set, the corresponding frame is used. */ 27 | bitmap: static Bitmap 28 | 29 | /// The last-used index in the Bitmap array. 30 | lastElement: static UInt 31 | 32 | /// Address used for pre-heap memory allocation. 33 | placementAddress := static Kernel end as SizeT 34 | 35 | /// Address space containing shared memory between processes. 36 | sharedSpace: static UInt* 37 | 38 | alloc: static func ~defaultAlignment (size: SizeT) -> Pointer { 39 | alloc(size, 1) 40 | } 41 | 42 | alloc: static func (size: SizeT, alignment: UInt16) -> Pointer { 43 | mem := null 44 | 45 | if(size == 0 || alignment == 0) { 46 | Bochs warn("Someone was smart enough to specify a length or alignment of 0!") 47 | return null 48 | } 49 | 50 | if(freeMemory < size) { 51 | Bochs warn("Not enough memory to allocate!") 52 | return null 53 | } 54 | 55 | // Placement allocation only allowed for the kernel when 56 | // dynamic memory is not available yet. 57 | if(alignment > 1) 58 | placementAddress = placementAddress align(alignment) 59 | 60 | mem = placementAddress as Pointer 61 | placementAddress += size 62 | usedMemory += size 63 | 64 | mem 65 | } 66 | 67 | free: static func (ptr: Pointer) { 68 | } 69 | 70 | allocFrame: static func -> UInt { 71 | // If we can't find a free frame, we will try again from the 72 | // beginning if the lastElement wasn't already 0. 73 | tryAgain := lastElement != 0 74 | 75 | for(elem in lastElement..bitmap size) { 76 | if(bitmap allSet?(elem)) 77 | continue 78 | 79 | for(bit in 0..32) { 80 | if(bitmap clear?(elem, bit)) { 81 | // We've found ourselves a free bit, allocate and return it. 82 | bitmap set(elem, bit) 83 | lastElement = elem 84 | return elem * 32 + bit 85 | } 86 | } 87 | 88 | lastElement += 1 89 | } 90 | 91 | // Maybe some frames we've already looked through have become available 92 | if(tryAgain) { 93 | lastElement = 0 94 | return allocFrame() 95 | } 96 | 97 | // If still nothing was found, the entire bitmap is set, and there is 98 | // no free memory! 99 | panic("The physical memory manager did not find any free physical frames!") 100 | return 0 101 | } 102 | 103 | allocFrame: static func ~address (address: SizeT) { 104 | address /= FRAME_SIZE 105 | bitmap set(address / 32, address % 32) 106 | } 107 | 108 | freeFrame: static func (address: SizeT) { 109 | address /= FRAME_SIZE 110 | bitmap clear(address / 32, address % 32) 111 | } 112 | 113 | setup: static func { 114 | // memUpper and memLower are given in kB, but we want B 115 | memorySize = (multiboot memLower + multiboot memUpper) * 1024 116 | 117 | frameCount := memorySize / FRAME_SIZE 118 | 119 | // 4 bytes equals 32 bits, use 1 bit per frame. 120 | elementCount := frameCount / 32 121 | 122 | // Add an extra element for the remainder of the frames if not aligned 123 | if(frameCount % 32) { 124 | elementCount += 1 125 | } 126 | 127 | bitmap = Bitmap new(elementCount) 128 | 129 | // If the last element used less than 32 bits, set the other bits to 130 | // make sure the allocator doesn't try to use them. 131 | for(i in (frameCount % 32)..32) { 132 | bitmap set(bitmap size - 1, i) 133 | } 134 | 135 | Bochs debug("Memory size: %i kB" format(multiboot memLower + multiboot memUpper)) 136 | Bochs debug("Bitmap size: %i B" format(bitmap size * 4)) 137 | 138 | // Map the shared virtual address space to itself. 139 | sharedSpace = alloc(FRAME_SIZE, FRAME_SIZE) 140 | memset(sharedSpace, 0 as UInt8, FRAME_SIZE) 141 | 142 | sharedSpace[1023] = (sharedSpace as SizeT) | 4 | 2 | 1 143 | 144 | placementAddress = placementAddress align(FRAME_SIZE) 145 | 146 | pageTable := null as UInt* 147 | physAddr := placementAddress 148 | 149 | i := 0 as SizeT 150 | while(i < placementAddress) { 151 | // Need to move to the next page table? 152 | if((i / FRAME_SIZE) % 1024 == 0) { 153 | // Allocate a frame for this page table and map it. 154 | allocFrame(physAddr) 155 | sharedSpace[i / FRAME_SIZE / 1024] = physAddr | 4 | 2 | 1 156 | 157 | pageTable = physAddr as UInt* 158 | memset(pageTable, 0 as UInt8, FRAME_SIZE) 159 | 160 | physAddr += FRAME_SIZE 161 | } 162 | 163 | // Identity map this region. 164 | allocFrame(i) 165 | pageTable[i / FRAME_SIZE] = i | 4 | 2 | 1 166 | 167 | i += FRAME_SIZE 168 | } 169 | 170 | // NOTE: All memory up until the page table containing physAddr is seen as allocated. 171 | placementAddress = physAddr align(0x400000) 172 | usedMemory = placementAddress 173 | 174 | // Parse the memory map from GRUB. 175 | parseMemoryMap() 176 | 177 | // Bochs debug("Taking the plunge...") 178 | // Switch and activate paging. 179 | // switchAddressSpace(sharedSpace) 180 | // Bochs debug("Swan dive!") 181 | 182 | // activatePaging() 183 | // Bochs debug("A perfect entry!") 184 | } 185 | 186 | parseMemoryMap: static func { 187 | i := multiboot mmapAddr 188 | 189 | "Memory map:" println() 190 | 191 | while(i < multiboot mmapAddr + multiboot mmapLength) { 192 | mmapEntry := i as MMapEntry* 193 | 194 | "0x%08x-0x%08x (%s)" printfln( 195 | mmapEntry@ baseAddrLow, 196 | mmapEntry@ baseAddrLow + mmapEntry@ lengthLow - 1, 197 | mmapEntry@ type == 1 ? "Available" : "Reserved") 198 | 199 | // Anything other than 1 means reserved. Mark every frame in this 200 | // region as used. 201 | if(mmapEntry@ type != 1) { 202 | j := mmapEntry@ baseAddrLow 203 | while(j < mmapEntry@ baseAddrLow + mmapEntry@ lengthLow) { 204 | allocFrame(j) 205 | 206 | // Only if it hasn't been included already. 207 | if(j >= placementAddress) 208 | usedMemory += FRAME_SIZE 209 | 210 | j += FRAME_SIZE 211 | } 212 | } 213 | 214 | i += mmapEntry@ size + mmapEntry@ size class size 215 | } 216 | 217 | '\n' print() 218 | } 219 | 220 | activatePaging: static func { 221 | setCR0(getCR0() bitSet(31)) 222 | } 223 | 224 | switchAddressSpace: static extern proto func (addressSpace: UInt*) 225 | getCR0: static extern proto func -> SizeT 226 | setCR0: static extern proto func (cr0: SizeT) 227 | } 228 | -------------------------------------------------------------------------------- /sdk/lang/types.ooc: -------------------------------------------------------------------------------- 1 | import devices/Display, Panic, Printf 2 | 3 | include stdbool, stdint, c_types 4 | include ./array 5 | 6 | /** 7 | * objects 8 | */ 9 | Object: abstract class { 10 | 11 | class: Class 12 | 13 | /// Instance initializer: set default values for a new instance of this class 14 | __defaults__: func {} 15 | 16 | /// Finalizer: cleans up any objects belonging to this instance 17 | __destroy__: func {} 18 | 19 | /** return true if *class* is a subclass of *T*. */ 20 | instanceOf: final func (T: Class) -> Bool { 21 | if(!this) return false 22 | class inheritsFrom(T) 23 | } 24 | 25 | /* 26 | toString: func -> String { 27 | "%s@%08p" format(class name, this) 28 | } 29 | */ 30 | } 31 | 32 | Class: abstract class { 33 | 34 | /// Number of octets to allocate for a new instance of this class 35 | instanceSize: SizeT 36 | 37 | /** 38 | * Number of octets to allocate to hold an instance of this class 39 | * it's different because for classes, instanceSize may greatly 40 | * vary, but size will always be equal to the size of a Pointer. 41 | * for basic types (e.g. Int, Char, Pointer), size == instanceSize 42 | */ 43 | size: SizeT 44 | 45 | /// Human readable representation of the name of this class 46 | name: String 47 | 48 | /// Pointer to instance of super-class 49 | super: const Class 50 | 51 | /// Create a new instance of the object of type defined by this class 52 | alloc: final func ~_class -> Object { 53 | object := gc_malloc(instanceSize) as Object 54 | if(object) { 55 | object class = this 56 | } 57 | return object 58 | } 59 | 60 | inheritsFrom: final func ~_class (T: Class) -> Bool { 61 | if(this == T) return true 62 | return (super ? super inheritsFrom(T) : false) 63 | } 64 | 65 | } 66 | 67 | Array: cover from _lang_array__Array { 68 | length: extern Int 69 | data: extern Pointer 70 | } 71 | 72 | /** 73 | * Pointer type 74 | */ 75 | Void: cover from void 76 | Pointer: cover from void* { 77 | toString: func -> String { "%p" format(this) } 78 | } 79 | 80 | NULL: unmangled Pointer = 0 81 | 82 | /** 83 | * character and pointer types 84 | */ 85 | //__char: extern(char) Void 86 | __char_ary: extern(__CHAR_ARY) Void 87 | Char: cover from char { 88 | /// check for an alphanumeric character 89 | alphaNumeric?: func -> Bool { 90 | alpha?() || digit?() 91 | } 92 | 93 | /// check for an alphabetic character 94 | alpha?: func -> Bool { 95 | lower?() || upper?() 96 | } 97 | 98 | /// check for a lowercase alphabetic character 99 | lower?: func -> Bool { 100 | this >= 'a' && this <= 'z' 101 | } 102 | 103 | /// check for an uppercase alphabetic character 104 | upper?: func -> Bool { 105 | this >= 'A' && this <= 'Z' 106 | } 107 | 108 | /// check for a decimal digit (0 through 9) 109 | digit?: func -> Bool { 110 | this >= '0' && this <= '9' 111 | } 112 | 113 | /// check for a hexadecimal digit (0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F) 114 | hexDigit?: func -> Bool { 115 | digit?() || 116 | (this >= 'A' && this <= 'F') || 117 | (this >= 'a' && this <= 'f') 118 | } 119 | 120 | /// check for a control character 121 | control?: func -> Bool { 122 | (this >= 0 && this <= 31) || this == 127 123 | } 124 | 125 | /// check for any printable character except space 126 | graph?: func -> Bool { 127 | printable?() && this != ' ' 128 | } 129 | 130 | /// check for any printable character including space 131 | printable?: func -> Bool { 132 | this >= 32 && this <= 126 133 | } 134 | 135 | /// check for any printable character which is not a space or an alphanumeric character 136 | punctuation?: func -> Bool { 137 | printable?() && !alphaNumeric?() && this != ' ' 138 | } 139 | 140 | /** check for white-space characters: space, form-feed ('\f'), newline ('\n'), 141 | carriage return ('\r'), horizontal tab ('\t'), and vertical tab ('\v') */ 142 | whitespace?: func -> Bool { 143 | this == ' ' || 144 | this == '\f' || 145 | this == '\n' || 146 | this == '\r' || 147 | this == '\t' || 148 | this == '\v' 149 | } 150 | 151 | /// check for a blank character; that is, a space or a tab 152 | blank?: func -> Bool { 153 | this == ' ' || this == '\t' 154 | } 155 | 156 | /// convert to an integer. This only works for digits, otherwise -1 is returned 157 | toInt: func -> Int { 158 | if (digit?()) { 159 | return (this - '0') as Int 160 | } 161 | return -1 162 | } 163 | 164 | /// return the lowered charater 165 | toLower: func -> This { 166 | if(this upper?()) { 167 | return this - 'A' + 'a' 168 | // or this + 32 ? 169 | } 170 | return this 171 | } 172 | 173 | /// return the capitalized character 174 | toUpper: func -> This { 175 | if(this lower?()) { 176 | return this - 'a' + 'A' 177 | // or this - 32 ? 178 | } 179 | return this 180 | } 181 | 182 | /// return a one-character string containing this character 183 | toString: func -> String { 184 | String new(this) 185 | } 186 | 187 | /// print this character without a following newline 188 | print: func { 189 | Display printChar(this) 190 | } 191 | 192 | /// print this character with a following newline 193 | println: func { 194 | Display printChar(this) 195 | Display printChar('\n') 196 | } 197 | } 198 | 199 | SChar: cover from signed char extends Char 200 | UChar: cover from unsigned char extends Char 201 | 202 | String: cover from Char* { 203 | /** Create a new string exactly *length* characters long (without the nullbyte). 204 | The contents of the string are undefined. */ 205 | new: static func~withLength (length: Int) -> This { 206 | result := gc_malloc(length + 1) as This 207 | result[length] = '\0' 208 | result 209 | } 210 | 211 | /// Create a new string of the length 1 containing only the character *c* 212 | new: static func~withChar (c: Char) -> This { 213 | result := This new~withLength(1) 214 | result[0] = c 215 | result 216 | } 217 | 218 | /// return the string's length, excluding the null byte. 219 | length: func -> SizeT { 220 | i := 0 221 | while (this[i]) 222 | i += 1 223 | return i 224 | } 225 | 226 | println: func { 227 | println(this) 228 | } 229 | 230 | print: func { 231 | print(this) 232 | } 233 | 234 | /** return a string formatted using *this* as template. */ 235 | format: func (...) -> This { 236 | list:VaList 237 | 238 | va_start(list, this) 239 | 240 | length := vsnprintf(null, 0, this, list) 241 | output := This new(length) 242 | va_end(list) 243 | 244 | va_start(list, this) 245 | vsnprintf(output, length + 1, this, list) 246 | va_end(list) 247 | 248 | return output 249 | } 250 | 251 | printf: func (...) { 252 | list: VaList 253 | 254 | va_start(list, this) 255 | vprintf(this, list) 256 | va_end(list) 257 | } 258 | 259 | printfln: func (...) { 260 | list: VaList 261 | 262 | va_start(list, this) 263 | vprintf(this, list) 264 | va_end(list) 265 | '\n' print() 266 | } 267 | } 268 | 269 | /** 270 | * integer types 271 | */ 272 | __void: extern(void) Void 273 | __int: extern(int) Void 274 | __uint: extern(uint32_t) Void 275 | 276 | Long: cover from signed long { 277 | toString: func -> String { "%ld" format(this) } 278 | toHexString: func -> String { "%lx" format(this) } 279 | 280 | odd?: func -> Bool { this % 2 == 1 } 281 | even?: func -> Bool { this % 2 == 0 } 282 | 283 | in?: func(range: Range) -> Bool { 284 | return this >= range min && this < range max 285 | } 286 | 287 | /// check if the specified `bit` is 1 288 | bitSet?: func (bit: UInt) -> Bool { 289 | (this & (1 << bit)) as Bool 290 | } 291 | 292 | /// check if the specified `bit` is 0 293 | bitClear?: func (bit: UInt) -> Bool { 294 | (~this & (1 << bit)) as Bool 295 | } 296 | 297 | /// set the specified `bit` to 1 298 | bitSet: func (bit: UInt) -> This { 299 | this | (1 << bit) 300 | } 301 | 302 | /// clear the specified `bit` to 0 303 | bitClear: func (bit: UInt) -> This { 304 | this & ~(1 << bit) 305 | } 306 | 307 | /// returns `this` aligned to the next boundary of `alignment`. 308 | align: func (alignment: This) -> This { 309 | if(this % alignment == 0) 310 | return this 311 | else 312 | return (this & ~(alignment - 1)) + alignment 313 | } 314 | } 315 | 316 | Int: cover from signed int extends Long 317 | Short: cover from signed short extends Long 318 | 319 | ULong: cover from unsigned long extends Long { 320 | toString: func -> String { "%lu" format(this) } 321 | } 322 | 323 | UInt: cover from unsigned int extends ULong 324 | UShort: cover from unsigned short extends ULong 325 | 326 | /** 327 | * fixed-size integer types 328 | */ 329 | Int8: cover from int8_t extends Long 330 | Int16: cover from int16_t extends Long 331 | Int32: cover from int32_t extends Long 332 | Int64: cover from int64_t extends Long 333 | 334 | UInt8: cover from uint8_t extends ULong 335 | UInt16: cover from uint16_t extends ULong 336 | UInt32: cover from uint32_t extends ULong 337 | UInt64: cover from uint64_t extends ULong 338 | 339 | SizeT: cover from size_t extends ULong 340 | SSizeT: cover from signed int extends Long 341 | 342 | Bool: cover from bool { 343 | toString: func -> String { this ? "true" : "false" } 344 | } 345 | 346 | /** 347 | * real types 348 | */ 349 | LDouble: cover from long double { 350 | toString: func -> String { 351 | "%.2Lf" format(this) 352 | } 353 | 354 | abs: func -> This { 355 | return this < 0 ? -this : this 356 | } 357 | } 358 | 359 | Float: cover from float extends LDouble 360 | Double: cover from double extends LDouble 361 | 362 | /** 363 | * custom types 364 | */ 365 | Range: cover { 366 | min, max: Int 367 | 368 | new: static func (.min, .max) -> This { 369 | this: This 370 | this min = min 371 | this max = max 372 | return this 373 | } 374 | } 375 | 376 | /** 377 | * exceptions 378 | */ 379 | Exception: class { 380 | 381 | origin: Class 382 | msg: String 383 | 384 | init: func ~originMsg (=origin, =msg) {} 385 | init: func ~noOrigin (=msg) {} 386 | 387 | getMessage: func -> String { 388 | if(origin) 389 | return "[%s in %s]: %s\n" format(this as Object class name, origin name, msg) 390 | else 391 | return "[%s]: %s\n" format(this as Object class name, msg) 392 | } 393 | 394 | throw: func { 395 | panic(getMessage()) 396 | } 397 | 398 | } 399 | 400 | /** 401 | * Closures 402 | */ 403 | Closure: cover { 404 | thunk: Pointer 405 | context: Pointer 406 | } 407 | -------------------------------------------------------------------------------- /src/Printf.ooc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2010 Nick Markwell and Scott Olson 3 | Copyright (c) 2009 Martin Brandenburg 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | import devices/Display 28 | 29 | /* Text Formatting */ 30 | TF_ALTERNATE := 1 << 0 31 | TF_ZEROPAD := 1 << 1 32 | TF_LEFT := 1 << 2 33 | TF_SPACE := 1 << 3 34 | TF_EXP_SIGN := 1 << 4 35 | TF_SMALL := 1 << 5 36 | TF_PLUS := 1 << 6 37 | TF_UNSIGNED := 1 << 7 38 | 39 | m_printn: func (str: String, maxlen, len: Int, n: UInt, base, size, flags, precision: Int) -> Int { 40 | sign: Char = '\0' 41 | tmp: Char[36] 42 | digits := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" 43 | i := 0 44 | signed_n := n as Int 45 | 46 | /* Preprocess the flags. */ 47 | 48 | if(flags & TF_ALTERNATE && base == 16) { 49 | str[len] = '0' 50 | if(flags & TF_SMALL) 51 | str[len + 1] = 'x' 52 | else 53 | str[len + 1] = 'X' 54 | len += 2 55 | } 56 | 57 | if(flags & TF_SMALL) 58 | digits = "0123456789abcdefghijklmnopqrstuvwxyz" 59 | 60 | if(!(flags & TF_UNSIGNED) && signed_n < 0) { 61 | sign = '-' 62 | n = -signed_n 63 | } else if(flags & TF_EXP_SIGN) { 64 | sign = '+' 65 | } 66 | 67 | if(sign) 68 | size -= 1 69 | 70 | /* Find the number in reverse. */ 71 | if(n == 0) { 72 | tmp[i] = '0' 73 | i += 1 74 | } else { 75 | while(n != 0) { 76 | tmp[i] = digits[n % base] 77 | i += 1 78 | n /= base 79 | } 80 | } 81 | 82 | /* Pad the number with zeros or spaces. */ 83 | if(!(flags & TF_LEFT)) 84 | while(size > i) { 85 | size -= 1 86 | if(flags & TF_ZEROPAD) { 87 | if(len < maxlen) { 88 | str[len] = '0' 89 | len += 1 90 | } else { 91 | len += 1 92 | } 93 | } else { 94 | if(len < maxlen) { 95 | str[len] = ' ' 96 | len += 1 97 | } else { 98 | len += 1 99 | } 100 | } 101 | } 102 | 103 | if(sign) { 104 | str[len] = sign 105 | len += 1 106 | } 107 | 108 | /* Write any zeros to satisfy the precision. */ 109 | while(i < precision) { 110 | precision -= 1 111 | if(len < maxlen) { 112 | str[len] = '0' 113 | len += 1 114 | } else { 115 | len += 1 116 | } 117 | } 118 | 119 | /* Write the number. */ 120 | while(i != 0) { 121 | i -= 1 122 | size -= 1 123 | if(len < maxlen) { 124 | str[len] = tmp[i] 125 | len += 1 126 | } else { 127 | len += 1 128 | } 129 | } 130 | 131 | /* Left align the numbers. */ 132 | if(flags & TF_LEFT) 133 | while(size > 0) { 134 | size -= 1 135 | if(len < maxlen) { 136 | str[len] = ' ' 137 | len += 1 138 | } else 139 | len += 1 140 | } 141 | 142 | return len 143 | } 144 | 145 | printf: func (fmt: String, ...) -> Int { 146 | str: String 147 | args: VaList 148 | len: Int 149 | 150 | va_start(args, fmt) 151 | len = vsnprintf(NULL, 0, fmt, args) 152 | va_end(args) 153 | 154 | str = gc_malloc(len+1) 155 | 156 | va_start(args, fmt) 157 | len = vsnprintf(str, len+1, fmt, args) 158 | va_end(args) 159 | 160 | str print() 161 | 162 | free(str) 163 | 164 | return len 165 | } 166 | 167 | sprintf: func (str: String, fmt: String, ...) -> Int { 168 | args: VaList 169 | i: Int 170 | 171 | va_start(args, fmt) 172 | i = vsnprintf(str, 0, fmt, args) 173 | va_end(args) 174 | 175 | va_start(args, fmt) 176 | i = vsnprintf(str, i + 1, fmt, args) 177 | va_end(args) 178 | 179 | return i 180 | } 181 | 182 | snprintf: func (str: String, size: SizeT, fmt: String, ...) -> Int { 183 | args: VaList 184 | i: Int 185 | 186 | va_start(args, fmt) 187 | i = vsnprintf(str, size, fmt, args) 188 | va_end(args) 189 | return i 190 | } 191 | 192 | vprintf: func (fmt: String, ap: VaList) -> Int { 193 | str: String 194 | len: Int 195 | 196 | len = vsnprintf(NULL, 0, fmt, ap) 197 | str = gc_malloc(len+1) 198 | len = vsnprintf(str, len+1, fmt, ap) 199 | 200 | str print() 201 | 202 | free(str) 203 | 204 | return len 205 | } 206 | 207 | vsprintf: func (str: String, fmt: String, ap: VaList) -> Int { 208 | i: Int 209 | 210 | i = vsnprintf(str, 0, fmt, ap) 211 | i = vsnprintf(str, i + 1, fmt, ap) 212 | return i 213 | } 214 | 215 | vsnprintf: func (str: String, size: SizeT, fmt: String, ap: VaList) -> Int { 216 | len := 0 217 | p: Char* 218 | flags, fieldwidth, precision, i: Int 219 | sval: Char* 220 | 221 | /* Leave room for the null byte. */ 222 | if(size != 0) 223 | size -= 1 224 | 225 | p = fmt 226 | while(p@) { 227 | if(p@ != '%') { 228 | if(len < size) { 229 | str[len] = p@ 230 | } 231 | len += 1 232 | p += 1 233 | continue 234 | } 235 | 236 | /* Find any flags. */ 237 | flags = 0 238 | 239 | while(true) { 240 | p += 1 241 | match(p@) { 242 | case '#' => flags |= TF_ALTERNATE 243 | case '0' => flags |= TF_ZEROPAD 244 | case '-' => flags |= TF_LEFT 245 | case ' ' => flags |= TF_SPACE 246 | case '+' => flags |= TF_EXP_SIGN 247 | case => break 248 | } 249 | } 250 | 251 | /* Find the field width. */ 252 | fieldwidth = 0 253 | while(p@ digit?()) { 254 | if(fieldwidth > 0) 255 | fieldwidth *= 10 256 | fieldwidth += (p@ - 0x30) as Int 257 | p += 1 258 | } 259 | 260 | /* Find the precision. */ 261 | precision = -1 262 | if(p@ == '.') { 263 | p += 1 264 | precision = 0 265 | if(p@ == '*') { 266 | precision = va_arg(ap, __int) 267 | p += 1 268 | } 269 | while(p@ digit?()) { 270 | if (precision > 0) 271 | precision *= 10 272 | precision += (p@ - 0x30) as Int 273 | p += 1 274 | } 275 | } 276 | 277 | /* Find the length modifier. */ 278 | if(p@ == 'l' || p@ == 'h' || p@ == 'L') { 279 | p += 1 280 | } 281 | 282 | flags |= TF_UNSIGNED 283 | /* Find the conversion. */ 284 | match(p@) { 285 | case 'i' => 286 | flags &= ~TF_UNSIGNED 287 | len = m_printn(str, size, len, 288 | va_arg(ap, __int), 10, 289 | fieldwidth, flags, precision) 290 | case 'd' => 291 | flags &= ~TF_UNSIGNED 292 | len = m_printn(str, size, len, 293 | va_arg(ap, __int), 10, 294 | fieldwidth, flags, precision) 295 | case 'o' => 296 | len = m_printn(str, size, len, 297 | va_arg(ap, __uint), 8, 298 | fieldwidth, flags, precision) 299 | case 'u' => 300 | len = m_printn(str, size, len, 301 | va_arg(ap, __uint), 10, 302 | fieldwidth, flags, precision) 303 | case 'x' => 304 | len = m_printn(str, size, len, 305 | va_arg(ap, __uint), 16, 306 | fieldwidth, flags|TF_SMALL, precision) 307 | case 'X' => 308 | len = m_printn(str, size, len, 309 | va_arg(ap, __uint), 16, 310 | fieldwidth, flags, precision) 311 | case 'p' => 312 | flags |= TF_ALTERNATE 313 | flags |= TF_SMALL 314 | len = m_printn(str, size, len, 315 | va_arg(ap, __uint), 16, 316 | fieldwidth, flags, precision) 317 | case 'c' => 318 | i = 0 319 | if(!(flags & TF_LEFT)) 320 | while(i < fieldwidth) { 321 | i += 1 322 | if(len < size) { 323 | str[len] = ' ' 324 | len += 1 325 | } else { 326 | len += 1 327 | } 328 | } 329 | if(len < size) { 330 | str[len] = va_arg(ap, __int) as UChar 331 | len += 1 332 | } else { 333 | len += 1 334 | va_arg(ap, __void) 335 | } 336 | while(i < fieldwidth) { 337 | i += 1 338 | if (len < size) { 339 | str[len] = ' ' 340 | len += 1 341 | } else { 342 | len += 1 343 | } 344 | } 345 | case 's' => 346 | sval = va_arg(ap, __char_ary) as Pointer 347 | /* Change to -2 so that 0-1 doesn't cause the 348 | * loop to keep going. */ 349 | if(precision == -1) 350 | precision = -2 351 | while(sval@ && (precision > 0 || precision <= -2)) { 352 | if(precision > 0) { 353 | precision -= 1 354 | } 355 | if(len < size) { 356 | str[len] = sval@ 357 | len += 1 358 | sval += 1 359 | } else { 360 | sval += 1 361 | len += 1 362 | } 363 | } 364 | case '%' => 365 | if(len < size) { 366 | str[len] = '%' 367 | len += 1 368 | } else { 369 | len += 1 370 | } 371 | case => 372 | if(len < size) { 373 | str[len] = p@ 374 | len += 1 375 | } else { 376 | len += 1 377 | } 378 | } 379 | p += 1 380 | } 381 | 382 | /* And now we magically have room for one more byte. */ 383 | if(size != 0) 384 | size += 1 385 | 386 | if(len < size) 387 | str[len] = '\0' 388 | else if(size != 0) 389 | str[size] = '\0' 390 | return len 391 | } 392 | --------------------------------------------------------------------------------