├── .gitignore ├── kernel ├── stl │ ├── utility │ ├── README.md │ ├── xstddef │ ├── xutility │ ├── new │ ├── allocator │ ├── pair │ ├── algorithm │ └── vector ├── include │ ├── stdio.h │ ├── abi.h │ ├── stdlib.h │ ├── linkage.h │ ├── panic.h │ ├── string.h │ ├── memory.h │ ├── assert.h │ ├── malloc.h │ ├── timer.h │ ├── stdint.h │ ├── cpu.h │ ├── system.h │ ├── systime.h │ ├── console.h │ └── port.h ├── mm │ ├── sbrk.h │ ├── log.cc │ ├── sbrk.cc │ ├── config.h │ └── init.cc ├── krnl │ ├── cpu │ │ ├── power.cc │ │ └── manipulate.cc │ ├── panic.cc │ ├── irq_handler.asm │ ├── abi │ │ ├── ctor.cc │ │ └── dtor.cc │ ├── isr_handler.asm │ ├── idt.cc │ ├── gdt.cc │ ├── systime.cc │ ├── timer.cc │ ├── isr.cc │ ├── console.cc │ └── irq.cc ├── util │ ├── rand.cc │ ├── string.cc │ ├── xprintf.h │ └── xprintf.cc ├── tmppgdir.c ├── ba │ ├── stream.cc │ ├── badapple.h │ ├── main.cc │ ├── decompressor.cc │ └── video.cc ├── init.cc ├── begin.asm ├── link.ld └── Makefile ├── grub.cfg ├── boot ├── Makefile ├── print.asm ├── gdt.asm ├── boot.asm └── disk.asm ├── docker └── Dockerfile ├── Makefile ├── link.py ├── LICENSE ├── README.md └── resource └── encode.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.bin 2 | *.elf 3 | *.img 4 | *.o 5 | dump.txt 6 | 7 | build/ 8 | -------------------------------------------------------------------------------- /kernel/stl/utility: -------------------------------------------------------------------------------- 1 | #ifndef __UTILITY__ 2 | #define __UTILITY__ 3 | 4 | #include 5 | 6 | #endif -------------------------------------------------------------------------------- /grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=3 2 | set default="0" 3 | menuentry "BadAppleOS" { 4 | multiboot /boot/kernel.elf 5 | } 6 | -------------------------------------------------------------------------------- /kernel/include/stdio.h: -------------------------------------------------------------------------------- 1 | #ifndef __STDIO_H__ 2 | #define __STDIO_H__ 3 | 4 | #include "../util/xprintf.h" 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /kernel/include/abi.h: -------------------------------------------------------------------------------- 1 | #ifndef __ABI_H__ 2 | #define __ABI_H__ 3 | 4 | namespace abi { 5 | 6 | void ctors(void); 7 | void dtors(void); 8 | 9 | } /* abi */ 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /kernel/include/stdlib.h: -------------------------------------------------------------------------------- 1 | #ifndef __STDLIB_H__ 2 | #define __STDLIB_H__ 3 | 4 | int rand(void); 5 | void srand(unsigned int seed); 6 | 7 | #define RAND_MAX 32767 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /kernel/mm/sbrk.h: -------------------------------------------------------------------------------- 1 | #ifndef __SBRK_H__ 2 | #define __SBRK_H__ 3 | 4 | #include 5 | 6 | namespace mm { 7 | 8 | void *sbrk(ptrdiff_t); 9 | 10 | } /* mm */ 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /kernel/include/linkage.h: -------------------------------------------------------------------------------- 1 | #ifndef __LINKAGE_H__ 2 | #define __LINKAGE_H__ 3 | 4 | #ifdef __cplusplus 5 | #define asmlinkage extern "C" 6 | #else 7 | #define asmlinkage 8 | #endif 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /kernel/include/panic.h: -------------------------------------------------------------------------------- 1 | #ifndef __PANIC_H__ 2 | #define __PANIC_H__ 3 | 4 | namespace panic { 5 | 6 | void panic(const char *, int); 7 | void panic(const char *); 8 | 9 | } /* panic */ 10 | 11 | #endif -------------------------------------------------------------------------------- /kernel/include/string.h: -------------------------------------------------------------------------------- 1 | #ifndef __STRING_H__ 2 | #define __STRING_H__ 3 | 4 | #include 5 | 6 | void *memset(void *, int, size_t); 7 | void *memcpy(void *, const void *, size_t); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /kernel/krnl/cpu/power.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | namespace cpu { 6 | 7 | namespace power { 8 | 9 | void reboot(void) { 10 | port::outb(0x64, 0xfe); 11 | } 12 | 13 | } /* power */ 14 | 15 | } /* cpu */ 16 | -------------------------------------------------------------------------------- /kernel/stl/README.md: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | 3 | * \ is written by [iceboy](https://github.com/iceboy-sjtu). 4 | * \ is copied from [here](http://stackoverflow.com/questions/11415082/malloc-free-based-stl-allocator). 5 | 6 | Thanks to these authors! -------------------------------------------------------------------------------- /kernel/util/rand.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | static unsigned long int next = 1; 5 | 6 | int rand(void) { 7 | next = next * 214013L + 2531011L; 8 | return (unsigned int) (next >> 16) & 0x7fff; 9 | } 10 | 11 | void srand(unsigned int seed) { 12 | next = seed; 13 | } 14 | -------------------------------------------------------------------------------- /boot/Makefile: -------------------------------------------------------------------------------- 1 | AS := nasm 2 | 3 | all: boot.bin 4 | 5 | build: 6 | mkdir -p ../build 7 | 8 | boot.bin: build boot.asm disk.asm print.asm 9 | $(AS) -o boot.bin boot.asm 10 | cp boot.bin ../build/boot.bin 11 | 12 | dump: 13 | ndisasm -b16 -o7C00h boot.bin > dump.txt 14 | 15 | clean: 16 | rm -f boot.bin 17 | 18 | .PHONY: all dump clean 19 | -------------------------------------------------------------------------------- /kernel/include/memory.h: -------------------------------------------------------------------------------- 1 | #ifndef __MEMORY_H__ 2 | #define __MEMORY_H__ 3 | 4 | #include 5 | 6 | namespace mm { 7 | 8 | void initialize(void); 9 | 10 | #define MM_LOG_SILENT 1 11 | #define MM_LOG_NOISY 0 12 | 13 | void log_status(int); 14 | void log_new(size_t, void *); 15 | void log_delete(void *); 16 | 17 | } /* mm */ 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /kernel/include/assert.h: -------------------------------------------------------------------------------- 1 | #ifndef __ASSERT_H__ 2 | #define __ASSERT_H__ 3 | 4 | #include 5 | 6 | #define STRINGIZE(x) DO_STRINGIZE(x) 7 | #define DO_STRINGIZE(x) #x 8 | 9 | #define assert(expr) (void)( (!!(expr)) || (panic::panic("Assertion " STRINGIZE(expr) " failed in file " __FILE__ ", line " STRINGIZE(__LINE__), 0), 0) ) 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /kernel/include/malloc.h: -------------------------------------------------------------------------------- 1 | #ifndef __MALLOC_H__ 2 | #define __MALLOC_H__ 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif /* __cplusplus */ 9 | 10 | void *malloc(size_t); 11 | void free(void *); 12 | void *calloc(size_t, size_t); 13 | void *realloc(void *, size_t); 14 | 15 | #ifdef __cplusplus 16 | } 17 | #endif /* __cplusplus */ 18 | 19 | #endif -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | 3 | RUN apt-get update && \ 4 | apt-get install --no-install-recommends -q -y grub-common grub-pc-bin xorriso && \ 5 | rm -rf /var/lib/apt/lists/* 6 | 7 | ENTRYPOINT ["/usr/bin/grub-mkrescue"] 8 | 9 | # docker build -t=grub:latest . 10 | # docker run -it --rm -v $(pwd):$(pwd) -w $(pwd) -u `id -u $USER` grub:latest -o kernel.iso iso 11 | -------------------------------------------------------------------------------- /boot/print.asm: -------------------------------------------------------------------------------- 1 | ; @function: print 2 | ; @brief: print a string to terminal. 3 | ; @parameters: pointer to string is passed through SI. 4 | print: 5 | pusha 6 | mov bx, si 7 | 8 | .loop: 9 | mov al, [bx] 10 | cmp al, 0x0 11 | je .fin 12 | mov ah, 0xe 13 | int 0x10 14 | inc bx 15 | jmp .loop 16 | 17 | .fin: 18 | ; print '\n' 19 | mov ah, 0x3 20 | xor bh, bh 21 | int 0x10 22 | inc dh 23 | xor dl, dl 24 | mov ah, 0x2 25 | int 0x10 26 | popa 27 | ret 28 | -------------------------------------------------------------------------------- /kernel/include/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef __TIMER_H__ 2 | #define __TIMER_H__ 3 | 4 | #include 5 | 6 | namespace timer { 7 | 8 | #define TIMER_INVALID_HANDLE NULL 9 | 10 | #define TIMER_TICK_PER_SECOND 18 11 | 12 | typedef void* handle_t; 13 | typedef void (* fn_timer_callback_t)(uint64_t, handle_t); 14 | 15 | void initialize(void); 16 | uint64_t get_system_tick(void); 17 | handle_t add(uint64_t, fn_timer_callback_t); 18 | bool remove(handle_t); 19 | 20 | } /* timer */ 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /kernel/util/string.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | void *memset(void *s, int v, size_t n) { 5 | __asm__ __volatile__ ( 6 | "cld\n\t" 7 | "rep\n\t" 8 | "stosb" 9 | : /* no outputs */ 10 | : "a"(v), "D"(s), "c"(n)); 11 | return s; 12 | } 13 | 14 | void *memcpy(void *d, const void *s, size_t n) { 15 | __asm__ __volatile__ ( 16 | "cld\n\t" 17 | "rep\n\t" 18 | "movsb" 19 | : /* no outputs */ 20 | : "c"(n), "S"(s), "D"(d)); 21 | return d; 22 | } 23 | -------------------------------------------------------------------------------- /kernel/mm/log.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | namespace mm { 6 | 7 | namespace { 8 | 9 | int silent = 0; 10 | 11 | } 12 | 13 | void log_status(int to) { 14 | silent = to; 15 | } 16 | 17 | void log_new(size_t n, void *p) { 18 | if (!silent) { 19 | printf("[new] %u bytes, pointer = 0x%x.\n", n, p); 20 | } 21 | } 22 | 23 | void log_delete(void *p) { 24 | if (!silent) { 25 | printf("[delete] pointer = 0x%x.\n", p); 26 | } 27 | } 28 | 29 | } /* mm */ 30 | -------------------------------------------------------------------------------- /kernel/include/stdint.h: -------------------------------------------------------------------------------- 1 | #ifndef __STDINT_H__ 2 | #define __STDINT_H__ 3 | 4 | typedef signed char int8_t; 5 | typedef unsigned char uint8_t; 6 | typedef short int16_t; 7 | typedef unsigned short uint16_t; 8 | typedef int int32_t; 9 | typedef unsigned int uint32_t; 10 | typedef long long int64_t; 11 | typedef unsigned long long uint64_t; 12 | 13 | typedef int intptr_t; 14 | typedef unsigned int uintptr_t; 15 | 16 | typedef int ptrdiff_t; 17 | 18 | typedef unsigned int size_t; 19 | 20 | #define NULL 0 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /kernel/stl/xstddef: -------------------------------------------------------------------------------- 1 | #ifndef __XSTDDEF__ 2 | #define __XSTDDEF__ 3 | 4 | namespace std { 5 | 6 | template 7 | struct binary_function { 8 | typedef arg_1 first_argument_type; 9 | typedef arg_2 second_argument_type; 10 | typedef result result_type; 11 | }; 12 | 13 | template 14 | struct less : public binary_function { // functor for operator< 15 | bool operator()(const type &left, const type &right) const { 16 | return left < right; 17 | } 18 | }; 19 | 20 | } 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /boot/gdt.asm: -------------------------------------------------------------------------------- 1 | gdt: 2 | 3 | .start: 4 | 5 | .null: 6 | dd 0x0 7 | dd 0x0 8 | 9 | .code: equ $ - gdt 10 | dw 0xffff ; limit 11 | dw 0x0 ; base_low 12 | db 0x0 ; base_middle 13 | db 10011010b ; access 14 | db 11001111b ; granularity 15 | db 0x0 ; base_high 16 | 17 | .data: equ $ - gdt 18 | dw 0xffff ; limit 19 | dw 0x0 ; base_low 20 | db 0x0 ; base_middle 21 | db 10010010b ; access 22 | db 11001111b ; granularity 23 | db 0x0 ; base_high 24 | 25 | .end: 26 | 27 | .descriptor: 28 | dw .end - .start - 1 ; limit 29 | dd .start ; base 30 | -------------------------------------------------------------------------------- /kernel/krnl/panic.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using console::setcolor; 8 | using console::mkcolor; 9 | using namespace console::vga_color; 10 | 11 | namespace panic { 12 | 13 | void panic(const char *what, int code) { 14 | setcolor(mkcolor(red, black), false); 15 | 16 | printf("Kernel panic, with"); 17 | if (what != NULL) { 18 | printf(" message: %s,", what); 19 | } 20 | printf(" code: %d.\n", code); 21 | 22 | cpu::manipulate::die(); 23 | } 24 | 25 | void panic(const char *what) { 26 | panic(what, 0); 27 | } 28 | 29 | } /* panic */ 30 | -------------------------------------------------------------------------------- /kernel/tmppgdir.c: -------------------------------------------------------------------------------- 1 | // This file should be compiled as C, as non-trivial designated initializers are 2 | // not supported in C++. 3 | 4 | #include 5 | 6 | #define HIGH_KERNEL 0xc0000000 7 | #define PAGE_SIZE 4096 8 | 9 | #define PAGE_PRESENT (1 << 0) 10 | #define PAGE_WRITE (1 << 1) 11 | #define PAGE_HUGE (1 << 7) 12 | 13 | __attribute__((__aligned__(PAGE_SIZE))) 14 | uint32_t tmp_pgdir[1024] = { 15 | // Map VA [0, 4MB) to PA [0, 4MB). 16 | [0] = 0x0 | PAGE_PRESENT | PAGE_WRITE | PAGE_HUGE, 17 | // Map VA [HIGH_KERNEL, HIGH_KERNEL + 4MB) to PA [0, 4MB). 18 | [HIGH_KERNEL >> 22] = 0x0 | PAGE_PRESENT | PAGE_WRITE | PAGE_HUGE, 19 | }; 20 | -------------------------------------------------------------------------------- /kernel/ba/stream.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "badapple.h" 3 | #include 4 | 5 | namespace badapple { 6 | 7 | stream::stream(const uint8_t *pool, const uint8_t *pool_end) : 8 | pool(pool), 9 | pool_end(pool_end), 10 | seek(pool), 11 | shift(0) { } 12 | 13 | bool stream::has_next() const { 14 | return seek < pool_end; 15 | } 16 | 17 | int stream::nextb() { 18 | if (!has_next()) { 19 | panic::panic("[stream] Out of bound."); 20 | } 21 | 22 | int bit = ((*seek) >> shift) & 1; 23 | shift += 1; 24 | if (shift == 8) { 25 | shift = 0; 26 | seek += 1; 27 | } 28 | return bit; 29 | } 30 | 31 | int stream::remain() const { 32 | return (pool_end - seek) * 8 - shift; 33 | } 34 | 35 | } /* badapple */ 36 | -------------------------------------------------------------------------------- /kernel/util/xprintf.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------*/ 2 | /* Universal string handler for user console interface (C)ChaN, 2011 */ 3 | /*------------------------------------------------------------------------*/ 4 | 5 | #ifndef _STRFUNC 6 | #define _STRFUNC 7 | 8 | #define va_start(v, l) __builtin_va_start(v, l) 9 | #define va_arg(v, l) __builtin_va_arg(v, l) 10 | #define va_end(v) __builtin_va_end(v) 11 | #define va_copy(d, s) __builtin_va_copy(d, s) 12 | typedef __builtin_va_list va_list; 13 | 14 | void putc (char c); 15 | void puts (const char* str); 16 | void printf (const char* fmt, ...); 17 | void sprintf (char* buff, const char* fmt, ...); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /kernel/include/cpu.h: -------------------------------------------------------------------------------- 1 | #ifndef __CPU_H__ 2 | #define __CPU_H__ 3 | 4 | #include "system.h" 5 | 6 | namespace cpu { 7 | 8 | namespace manipulate { 9 | 10 | void cli(void); 11 | void sti(void); 12 | void halt(void); 13 | void die(void); 14 | void sleep(uint64_t); 15 | 16 | /* Interruption guard (scoped/RAII). */ 17 | class int_guard { 18 | public: 19 | int_guard(); 20 | ~int_guard(); 21 | 22 | void acquire(void); /* clear interruption. */ 23 | void release(void); /* set interruption. */ 24 | bool owns(void) const; /* if already cleared interruption. */ 25 | 26 | private: 27 | bool cleared; 28 | }; 29 | 30 | } /* manipulate */ 31 | 32 | namespace power { 33 | 34 | void reboot(void); 35 | 36 | } /* power */ 37 | 38 | } /* cpu */ 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /kernel/mm/sbrk.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include "sbrk.h" 5 | 6 | namespace mm { 7 | 8 | namespace { 9 | 10 | void* const MFAIL = (void*) ~(size_t(0)); 11 | 12 | #define KRNL_MEM_BEGIN ((void*) 0xc0400000) 13 | #define KRNL_MEM_END ((void*) 0xc0c00000) 14 | 15 | uint8_t* pbreak = (uint8_t*) KRNL_MEM_BEGIN; 16 | 17 | } 18 | 19 | // implementation follows http://linux.die.net/man/2/sbrk. 20 | void *sbrk(ptrdiff_t increment) { 21 | auto ptr = pbreak; 22 | auto new_ptr = pbreak + increment; 23 | 24 | printf("[sbrk] increment: 0x%x.\n", increment); 25 | if (new_ptr < KRNL_MEM_BEGIN || new_ptr > KRNL_MEM_END) { 26 | puts("[sbrk] MFAIL."); 27 | return MFAIL; 28 | } 29 | pbreak = new_ptr; 30 | return ptr; 31 | } 32 | 33 | } /* mm */ 34 | -------------------------------------------------------------------------------- /kernel/krnl/irq_handler.asm: -------------------------------------------------------------------------------- 1 | [extern _irq_dispatcher] 2 | 3 | KRNL_DATA_SEL equ 0x10 4 | 5 | %macro irq 1 6 | global _irq_handler%1 7 | _irq_handler%1: 8 | cli 9 | push %1 ; irq index 10 | jmp irq_routine 11 | %endmacro 12 | 13 | irq 0 14 | irq 1 15 | irq 2 16 | irq 3 17 | irq 4 18 | irq 5 19 | irq 6 20 | irq 7 21 | irq 8 22 | irq 9 23 | irq 10 24 | irq 11 25 | irq 12 26 | irq 13 27 | irq 14 28 | irq 15 29 | 30 | irq_routine: 31 | pushad 32 | push ds 33 | push es 34 | push fs 35 | push gs 36 | mov eax, KRNL_DATA_SEL 37 | mov ds, eax 38 | mov es, eax 39 | mov fs, eax 40 | mov gs, eax 41 | push esp ; pointer to all stuffs we have pushed, wraps everything on 42 | ; stack into one struct pointer 43 | call _irq_dispatcher 44 | pop esp 45 | pop gs 46 | pop fs 47 | pop es 48 | pop ds 49 | popad 50 | add esp, 0x4 ; pop irq index 51 | iret 52 | -------------------------------------------------------------------------------- /kernel/krnl/abi/ctor.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | extern int32_t __INIT_ARRAY_LIST__; 7 | extern int32_t __CTOR_LIST__; 8 | 9 | typedef void (* fn_ptr) (void); 10 | 11 | namespace abi { 12 | 13 | namespace { 14 | 15 | fn_ptr *lpfn_inta_ptr = (fn_ptr *) &__INIT_ARRAY_LIST__; 16 | fn_ptr *lpfn_ctor_ptr = (fn_ptr *) &__CTOR_LIST__; 17 | 18 | int wkctor(fn_ptr *lpfn_ptr) { 19 | if (lpfn_ptr != NULL) { 20 | int ctor_count = (int) (*lpfn_ptr); 21 | for (int i = 1; i <= ctor_count; ++i) { 22 | printf("[ctor] Calling 0x%x.\n", lpfn_ptr); 23 | (*(lpfn_ptr + i))(); 24 | } 25 | return ctor_count; 26 | } 27 | return 0; 28 | } 29 | 30 | } 31 | 32 | void ctors(void) { 33 | int count = 0; 34 | 35 | puts("[ctor] Initializing."); 36 | count += wkctor(lpfn_inta_ptr); 37 | count += wkctor(lpfn_ctor_ptr); 38 | printf("[ctor] %d ctors initialized.\n", count); 39 | } 40 | 41 | } /* abi */ 42 | -------------------------------------------------------------------------------- /kernel/init.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | extern void play(void); 11 | 12 | namespace { 13 | 14 | void idle(void) { 15 | while (true) { 16 | cpu::manipulate::halt(); 17 | } 18 | } 19 | 20 | } 21 | 22 | asmlinkage void kinitialize(void) { 23 | /* console: no cursor, and no blink text. */ 24 | console::initialize(false); 25 | puts("Successfully landed to protected mode."); 26 | 27 | mm::initialize(); 28 | gdt::initialize(); 29 | idt::initialize(); 30 | isr::initialize(); 31 | irq::initialize(); 32 | timer::initialize(); 33 | 34 | /* enable interrupt. */ 35 | cpu::manipulate::sti(); 36 | 37 | /* call all C++ constructors. */ 38 | abi::ctors(); 39 | 40 | /* play our BadApple animation. */ 41 | play(); 42 | 43 | /* execute idle process. */ 44 | idle(); 45 | } 46 | 47 | -------------------------------------------------------------------------------- /kernel/mm/config.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONFIG_H__ 2 | #define __CONFIG_H__ 3 | 4 | #include 5 | #include "sbrk.h" 6 | 7 | #define fprintf(fd, format, args...) printf(format, ##args) 8 | 9 | #ifdef WIN32 10 | #undef WIN32 11 | #endif /* WIN32 */ 12 | 13 | #ifdef _WIN32 14 | #undef _WIN32 15 | #endif /* _WIN32 */ 16 | 17 | #define HAVE_MORECORE 1 18 | #define HAVE_MMAP 0 19 | #define USE_LOCKS 0 20 | 21 | #define ABORT panic::panic("Error in dlmalloc " __FILE__, __LINE__) 22 | #define EINVAL 0 23 | #define ENOMEM 0 24 | 25 | #define MORECORE mm::sbrk 26 | 27 | #define LACKS_UNISTD_H 28 | #define LACKS_SYS_PARAM_H 29 | #define LACKS_SYS_MMAN_H 30 | #define LACKS_SYS_TYPES_H 31 | #define LACKS_ERRNO_H 32 | #define LACKS_SCHED_H 33 | #define LACKS_TIME_H 34 | #define LACKS_STDIO_H 35 | #define LACKS_STDLIB_H 36 | 37 | #ifndef MALLOC_FAILURE_ACTION 38 | #define MALLOC_FAILURE_ACTION 39 | #endif /* MALLOC_FAILURE_ACTION */ 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /kernel/begin.asm: -------------------------------------------------------------------------------- 1 | [extern _kinitialize] 2 | [extern _tmp_pgdir] 3 | [bits 32] 4 | 5 | [section .mbheader] 6 | mbheader: 7 | dd 0x1badb002 8 | dd 0x0 9 | dd 0xe4524ffe 10 | 11 | KERNEL_LMA equ 0x10000 12 | KERNEL_VMA equ 0xc0010000 13 | CR0_PE equ 0x1 14 | CR0_PG equ 0x80000000 15 | CR4_PSE equ 0x10 16 | 17 | %define VA2PA(x) ((x) - KERNEL_VMA + KERNEL_LMA) 18 | 19 | [section .text] 20 | begin: 21 | ; initialize kernel stack (reuse memory below LMA) 22 | mov ebp, KERNEL_LMA 23 | mov esp, ebp 24 | 25 | ; turn on page size extension (PSE) for 4Mbytes pages 26 | mov eax, cr4 27 | or eax, CR4_PSE 28 | mov cr4, eax 29 | 30 | ; initialize paging, notice cr3 requires physical address 31 | mov eax, VA2PA(_tmp_pgdir) 32 | mov cr3, eax 33 | 34 | ; turn on paging 35 | mov eax, cr0 36 | or eax, CR0_PE | CR0_PG 37 | mov cr0, eax 38 | 39 | ; directly jump to C++ main, use "jmp eax" to avoid relative jump 40 | mov eax, _kinitialize 41 | jmp eax 42 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OSIMG := build/os.img 2 | OSISO_GRUB := build/os_grub.iso 3 | 4 | all: img qemu 5 | 6 | # necessary if we want to build `grub_iso`. 7 | docker: 8 | docker build -t=grub:latest docker 9 | 10 | img: boot kernel 11 | python link.py build/boot.bin build/kernel.bin $(OSIMG) 12 | 13 | grub_iso: kernel 14 | mkdir -p build/iso/boot/grub 15 | cp grub.cfg build/iso/boot/grub/grub.cfg 16 | cp build/kernel.elf build/iso/boot/kernel.elf 17 | docker run -it --rm -v $(shell pwd):$(shell pwd) -w $(shell pwd) -u `id -u $(shell USER)` grub:latest -o $(OSISO_GRUB) build/iso 18 | 19 | boot: 20 | cd boot && make 21 | 22 | kernel: 23 | cd kernel && make 24 | 25 | qemu: $(OSIMG) 26 | qemu-system-i386 -drive file=$(OSIMG),index=0,media=disk,format=raw 27 | 28 | qemu_grub: $(OSISO_GRUB) 29 | qemu-system-i386 -cdrom $(OSISO_GRUB) 30 | 31 | clean: 32 | cd boot && make clean 33 | cd kernel && make clean 34 | rm -rf build 35 | 36 | .PHONY: docker img grub_iso boot kernel qemu qemu_grub clean 37 | -------------------------------------------------------------------------------- /kernel/krnl/isr_handler.asm: -------------------------------------------------------------------------------- 1 | [extern _isr_dispatcher] 2 | 3 | %macro isr 1 4 | global _isr_handler%1 5 | _isr_handler%1: 6 | cli 7 | push 0 ; dummy error code 8 | push %1 ; exception index 9 | jmp isr_routine 10 | %endmacro 11 | 12 | %macro isr_ec 1 ; with error code 13 | global _isr_handler%1 14 | _isr_handler%1: 15 | cli 16 | push %1 17 | jmp isr_routine 18 | %endmacro 19 | 20 | isr 0 21 | isr 1 22 | isr 2 23 | isr 3 24 | isr 4 25 | isr 5 26 | isr 6 27 | isr 7 28 | isr_ec 8 29 | isr 9 30 | isr_ec 10 31 | isr_ec 11 32 | isr_ec 12 33 | isr_ec 13 34 | isr_ec 14 35 | isr 15 36 | isr 16 37 | isr 17 38 | isr 18 39 | 40 | isr_routine: 41 | pushad 42 | push ds 43 | push es 44 | push fs 45 | push gs 46 | push esp ; pointer to all stuffs we have pushed, wraps everything on 47 | ; stack into one struct pointer 48 | call _isr_dispatcher 49 | pop esp 50 | pop gs 51 | pop fs 52 | pop es 53 | pop ds 54 | popad 55 | add esp, 0x8 ; pop error code & exception index 56 | iret 57 | -------------------------------------------------------------------------------- /kernel/stl/xutility: -------------------------------------------------------------------------------- 1 | #ifndef __XUTILITY__ 2 | #define __XUTILITY__ 3 | 4 | #include 5 | 6 | namespace std { 7 | 8 | template 9 | struct iterator_traits { 10 | // iterator_category not implemented 11 | typedef typename iterator::value_type value_type; 12 | typedef typename iterator::difference_type difference_type; 13 | typedef difference_type distance_type; 14 | typedef typename iterator::pointer pointer; 15 | typedef typename iterator::reference reference; 16 | }; 17 | 18 | template 19 | struct iterator_traits { 20 | typedef type value_type; 21 | typedef ptrdiff_t difference_type; 22 | typedef ptrdiff_t distance_type; 23 | typedef type *pointer; 24 | typedef type &reference; 25 | }; 26 | 27 | template 28 | struct iterator_traits { 29 | typedef type value_type; 30 | typedef ptrdiff_t difference_type; 31 | typedef ptrdiff_t distance_type; 32 | typedef const type *pointer; 33 | typedef const type &reference; 34 | }; 35 | 36 | } /* std */ 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /kernel/include/system.h: -------------------------------------------------------------------------------- 1 | #ifndef __SYSTEM_H__ 2 | #define __SYSTEM_H__ 3 | 4 | #include 5 | 6 | namespace gdt { 7 | 8 | #define KERNEL_CODE_SEL 0x8 9 | #define KERNEL_DATA_SEL 0x10 10 | 11 | void initialize(void); 12 | void set_gate(int, uint32_t, uint32_t, uint8_t, uint8_t); 13 | 14 | } /* gdt */ 15 | 16 | namespace idt { 17 | 18 | void initialize(void); 19 | void set_gate(uint8_t, uint32_t, uint16_t, uint8_t); 20 | 21 | } /* idt */ 22 | 23 | namespace isr { 24 | 25 | void initialize(void); 26 | 27 | } /* isr */ 28 | 29 | namespace irq { 30 | 31 | #pragma pack(push, 1) 32 | struct irq_context_t { 33 | uint32_t gs, fs, es, ds; 34 | uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; 35 | uint32_t irq_index; 36 | uint32_t eip, cs, eflags; 37 | } __attribute__((packed)); 38 | #pragma pack(pop) 39 | 40 | typedef void (* fn_irq_handler_t)(irq_context_t *); 41 | 42 | void initialize(void); 43 | void install(int, fn_irq_handler_t); 44 | void uninstall(int); 45 | void enable(int); 46 | void disable(int); 47 | 48 | } /* irq */ 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /link.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | 6 | 7 | def little_endian(n): 8 | xs = [] 9 | for i in range(0, 4): 10 | xs.append(n % 256) 11 | n //= 256 12 | return xs 13 | 14 | 15 | if __name__ == "__main__": 16 | boot = sys.argv[1] 17 | kernel = sys.argv[2] 18 | output = sys.argv[3] 19 | 20 | boot_size = os.stat(boot).st_size 21 | old_kernel_size = os.stat(kernel).st_size 22 | kernel_size = (old_kernel_size + 512 - 1) // 512 * 512 23 | 24 | if boot_size != 512: 25 | print("Size of boot loader is not 512.") 26 | sys.exit(1) 27 | 28 | print("Kernel size %d -> %d." % (old_kernel_size, kernel_size)) 29 | with open(kernel, "ab") as f: 30 | padding = kernel_size - old_kernel_size 31 | f.write(bytearray(padding)) 32 | 33 | print("Populating kernel_sectors in bootloader.") 34 | with open(boot, "r+b") as f: 35 | f.seek(2) 36 | byte2 = little_endian(kernel_size // 512)[:2] 37 | f.write(bytearray(byte2)) 38 | 39 | os.system("cat %s %s > %s" % (boot, kernel, output)) 40 | 41 | print("\033[92mDone!\033[0m") 42 | -------------------------------------------------------------------------------- /kernel/include/systime.h: -------------------------------------------------------------------------------- 1 | #ifndef __SYSTIME_H__ 2 | #define __SYSTIME_H__ 3 | 4 | namespace systime { 5 | 6 | struct systime_t { 7 | int year, month, day; 8 | int hour, minute, second; 9 | 10 | systime_t() { 11 | year = 2012, month = 12, day = 21; 12 | hour = 0xd, minute = 0xe, second = 0xa; // where is 0xd ? 13 | } 14 | 15 | friend bool operator == (const systime_t &, const systime_t &); 16 | friend bool operator != (const systime_t &, const systime_t &); 17 | }; 18 | 19 | inline bool operator == (const systime_t &a, const systime_t &b) { 20 | return a.second == b.second && 21 | a.minute == b.minute && 22 | a.hour == b.hour && 23 | a.day == b.day && 24 | a.month == b.month && 25 | a.year == b.year; 26 | } 27 | 28 | inline bool operator != (const systime_t &a, const systime_t &b) { 29 | return a.second != b.second || 30 | a.minute != b.minute || 31 | a.hour != b.hour || 32 | a.day != b.day || 33 | a.month != b.month || 34 | a.year != b.year; 35 | } 36 | 37 | 38 | systime_t *get_systime(systime_t *); 39 | 40 | } /* systime */ 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 foreverbell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /kernel/include/console.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONSOLE_H__ 2 | #define __CONSOLE_H__ 3 | 4 | #include 5 | 6 | namespace console { 7 | 8 | #define VIDEO_BASE 0xb8000 9 | #define VIDEO_MAX_ROW 25 10 | #define VIDEO_MAX_COLUMN 80 11 | #define VIDEO_SIZE (VIDEO_MAX_ROW * VIDEO_MAX_COLUMN) 12 | 13 | namespace vga_color { 14 | 15 | const int black = 0; 16 | const int blue = 1; 17 | const int green = 2; 18 | const int cyan = 3; 19 | const int red = 4; 20 | const int magenta = 5; 21 | const int brown = 6; 22 | const int light_grey = 7; 23 | const int dark_grey = 8; 24 | const int light_blue = 9; 25 | const int light_green = 10; 26 | const int light_cyan = 11; 27 | const int light_red = 12; 28 | const int light_magenta = 13; 29 | const int light_brown = 14; 30 | const int white = 15; 31 | 32 | } /* vga_color */ 33 | 34 | const int default_back_color = vga_color::black; 35 | const int default_fore_color = vga_color::light_grey; 36 | 37 | int mkcolor(int, int); // fore, back 38 | void setcolor(int, bool); 39 | void initialize(bool); 40 | void clear(void); 41 | void bkcopy(const uint16_t *); 42 | void putch(char); 43 | 44 | } /* console */ 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /kernel/link.ld: -------------------------------------------------------------------------------- 1 | phys = 0x10000; /* physical address of entry point. */ 2 | 3 | STARTUP(begin.o) 4 | 5 | SECTIONS 6 | { 7 | . = 0xc0010000; /* virtual address of kernel. */ 8 | 9 | /* AT(phys) for load memory address of this section. */ 10 | .mbheader ALIGN(4) : AT(phys) { 11 | mbheader = .; 12 | *(.mbheader) 13 | } 14 | 15 | .text ALIGN(4) : AT(phys + (code - mbheader)) { 16 | code = .; 17 | *(.text .text.*) 18 | *(.rodata .rodata.*) 19 | } 20 | 21 | .data ALIGN(4) : AT(phys + (data - mbheader)) { 22 | data = .; 23 | *(.data) 24 | } 25 | 26 | .ctors ALIGN(4) : AT(phys + (ctors - mbheader)) { 27 | ctors = .; 28 | ___CTOR_LIST__ = .; 29 | LONG((___CTOR_END__ - ___CTOR_LIST__) / 4 - 2) 30 | *(.ctors) 31 | LONG(0) 32 | ___CTOR_END__ = .; 33 | 34 | ___INIT_ARRAY_LIST__ = .; 35 | LONG((___INIT_ARRAY_END__ - ___INIT_ARRAY_LIST__) / 4 - 2) 36 | *(.init_array) 37 | LONG(0) 38 | ___INIT_ARRAY_END__ = .; 39 | } 40 | 41 | .bss ALIGN(4) : AT(phys + (bss - mbheader)) { 42 | bss = .; 43 | *(.bss) 44 | } 45 | 46 | /DISCARD/ : { 47 | *(.eh_frame .note.GNU-stack .comment) 48 | } 49 | 50 | end = .; 51 | } 52 | -------------------------------------------------------------------------------- /kernel/stl/new: -------------------------------------------------------------------------------- 1 | #ifndef __NEW__ 2 | #define __NEW__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace std { 10 | 11 | inline void bad_alloc(const char *what = NULL) { 12 | if (what == NULL) { 13 | panic::panic("Bad alloc."); 14 | } 15 | panic::panic(what); 16 | } 17 | 18 | } /* std */ 19 | 20 | inline void *operator new(size_t n) { 21 | void *p = malloc(n); 22 | mm::log_new(n, p); 23 | if (p == NULL) { 24 | std::bad_alloc(); 25 | } 26 | return p; 27 | } 28 | 29 | inline void *operator new [](size_t n) { 30 | void *p = malloc(n); 31 | mm::log_new(n, p); 32 | if (p == NULL) { 33 | std::bad_alloc(); 34 | } 35 | return p; 36 | } 37 | 38 | inline void operator delete(void *p) { 39 | mm::log_delete(p); 40 | return free(p); 41 | } 42 | 43 | inline void operator delete [](void *p) { 44 | mm::log_delete(p); 45 | return free(p); 46 | } 47 | 48 | /* placement new. */ 49 | inline void *operator new(size_t, void *p) { 50 | return p; 51 | } 52 | 53 | inline void *operator new [](size_t, void *p) { 54 | return p; 55 | } 56 | 57 | inline void operator delete(void *, void *) { } 58 | 59 | inline void operator delete [](void *, void *) { } 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /kernel/include/port.h: -------------------------------------------------------------------------------- 1 | #ifndef __PORT_H__ 2 | #define __PORT_H__ 3 | 4 | #include 5 | 6 | namespace port { 7 | 8 | #define PORT_CMOS_CMD 0x70 9 | #define PORT_CMOS_DAT 0x71 10 | 11 | inline uint8_t inb(uint16_t port) { 12 | uint8_t ret; 13 | __asm__ __volatile__ ( 14 | "inb %1, %0" 15 | : "=a"(ret) 16 | : "dN"(port) 17 | ); 18 | return ret; 19 | } 20 | 21 | inline uint16_t inw(uint16_t port) { 22 | uint16_t ret; 23 | __asm__ __volatile__ ( 24 | "inw %1, %0" 25 | : "=a"(ret) 26 | : "dN"(port) 27 | ); 28 | return ret; 29 | } 30 | 31 | inline void outb(uint16_t port, uint8_t data) { 32 | __asm__ __volatile__ ( 33 | "outb %1, %0" 34 | : 35 | : "dN"(port), "a"(data) 36 | ); 37 | } 38 | 39 | inline void outw(uint16_t port, uint16_t data) { 40 | __asm__ __volatile__ ( 41 | "outw %1, %0" 42 | : 43 | : "dN"(port),"a"(data) 44 | ); 45 | } 46 | 47 | inline void wait(void) { 48 | __asm__ __volatile__ ( 49 | "outb %%al, $0x80" 50 | : 51 | : "a"(0) 52 | ); 53 | } 54 | 55 | } /* port */ 56 | 57 | namespace cmos { 58 | 59 | inline uint8_t read(uint8_t r) { 60 | #ifdef NMI_DISABLE 61 | r |= (1 << 7); 62 | #endif 63 | port::outb(PORT_CMOS_CMD, r); 64 | return port::inb(PORT_CMOS_DAT); 65 | } 66 | 67 | } /* cmos */ 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /kernel/stl/allocator: -------------------------------------------------------------------------------- 1 | #ifndef __ALLOCATOR__ 2 | #define __ALLOCATOR__ 3 | 4 | #include 5 | 6 | namespace std { 7 | 8 | template 9 | struct allocator { 10 | typedef size_t size_type; 11 | typedef ptrdiff_t difference_type; 12 | typedef T* pointer; 13 | typedef const T* const_pointer; 14 | typedef T& reference; 15 | typedef const T& const_reference; 16 | typedef T value_type; 17 | 18 | template struct rebind { typedef allocator other; }; 19 | allocator() { } 20 | allocator(const allocator &) { } 21 | 22 | template allocator(const allocator &) {} 23 | 24 | ~allocator() {} 25 | 26 | pointer address(reference x) const { return &x; } 27 | const_pointer address(const_reference x) const { return &x; } 28 | 29 | pointer allocate(size_type s, void const * = 0) { 30 | if (s == 0) { 31 | return NULL; 32 | } 33 | pointer temp = new T[s]; 34 | if (temp == NULL) { 35 | std::bad_alloc(); 36 | } 37 | return temp; 38 | } 39 | 40 | void deallocate(pointer p, size_type) { 41 | delete [] p; 42 | } 43 | 44 | size_type max_size() const { 45 | return (~size_t(0)) / sizeof(T); 46 | } 47 | 48 | void construct(pointer p, const T &val) { 49 | new((void *)p) T(val); 50 | } 51 | 52 | void destroy(pointer p) { 53 | p->~T(); 54 | } 55 | }; 56 | 57 | } /* std */ 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /kernel/ba/badapple.h: -------------------------------------------------------------------------------- 1 | #ifndef __BADAPPLE_H__ 2 | #define __BADAPPLE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace badapple { 9 | 10 | class stream { 11 | public: 12 | stream(const uint8_t *, const uint8_t *); 13 | int nextb(); 14 | bool has_next() const; 15 | int remain() const; 16 | private: 17 | const uint8_t *pool, *pool_end, *seek; 18 | int shift; 19 | }; 20 | 21 | class decompressor { 22 | public: 23 | decompressor(const uint8_t *, const uint8_t *); 24 | ~decompressor(); 25 | 26 | int frame_count() const { return count; } 27 | const uint8_t *data() const { return buffer; } 28 | const uint8_t *data_end() const { return buffer_end; } 29 | 30 | decompressor(const decompressor &) = delete; 31 | decompressor &operator = (const decompressor &) = delete; 32 | private: 33 | int count; 34 | uint8_t *buffer, *buffer_end; 35 | }; 36 | 37 | class video { 38 | public: 39 | video(); 40 | int progress() const; 41 | void next(); 42 | bool has_next() const; 43 | void free(); 44 | private: 45 | void artify(); 46 | 47 | /* dsu */ 48 | struct info_t { 49 | char ch; 50 | int size; 51 | }; 52 | std::pair find(int); 53 | void join(int, int); 54 | private: 55 | uint16_t *pool; 56 | int count, cur_frame; 57 | std::vector> dsu; 58 | }; 59 | 60 | } /* badapple */ 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /kernel/krnl/idt.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | namespace idt { 6 | 7 | namespace { 8 | 9 | #pragma pack(push, 1) 10 | struct idt_entry_t { 11 | uint16_t base_low; 12 | uint16_t sel; 13 | uint8_t always0; 14 | uint8_t flags; 15 | uint16_t base_high; 16 | } __attribute__((packed)); 17 | 18 | /* flags: 19 | * 7 0 20 | +---+---+---+---+---+---+---+---+ 21 | | P | DPL | S | GateType | 22 | +---+---+---+---+---+---+---+---+ 23 | */ 24 | 25 | struct idt_descriptior_t { 26 | uint16_t limit; 27 | uint32_t base; 28 | } __attribute__((packed)); 29 | #pragma pack(pop) 30 | 31 | #define MAX_IDT_ENTRY 256 32 | 33 | idt_entry_t idt[MAX_IDT_ENTRY]; 34 | idt_descriptior_t idt_ptr; 35 | 36 | void flush(void) { 37 | __asm__ __volatile__ ("lidt %0" :"=m"(idt_ptr)); 38 | } 39 | 40 | } 41 | 42 | void set_gate(uint8_t index, uint32_t base, uint16_t sel, uint8_t flags) { 43 | idt[index].base_low = (base & 0xffff); 44 | idt[index].sel = sel; 45 | idt[index].always0 = 0; 46 | idt[index].flags = flags; 47 | idt[index].base_high = ((base >> 16) & 0xffff); 48 | } 49 | 50 | void initialize(void) { 51 | /* initialize all idt entries. */ 52 | memset(idt, 0, sizeof(idt)); 53 | 54 | /* initialize idt ptr. */ 55 | idt_ptr.limit = sizeof(idt) - 1; 56 | idt_ptr.base = (uint32_t) idt; 57 | 58 | flush(); 59 | } 60 | 61 | } /* idt */ 62 | 63 | -------------------------------------------------------------------------------- /kernel/ba/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "badapple.h" 10 | 11 | using systime::systime_t; 12 | using systime::get_systime; 13 | using timer::handle_t; 14 | 15 | namespace { 16 | 17 | badapple::video v; 18 | 19 | } 20 | 21 | void play(void) { 22 | const int fps = 9; 23 | 24 | timer::add(TIMER_TICK_PER_SECOND, [](uint64_t count, handle_t h) { 25 | if (count < 3) { 26 | printf("\rBadApple, %d second(s) to go", 3 - count); 27 | for (uint64_t i = 0; i <= count; ++i) { 28 | printf("."); 29 | } 30 | } else { 31 | timer::remove(h); 32 | timer::add(TIMER_TICK_PER_SECOND / fps, [](uint64_t, handle_t h) { 33 | if (v.has_next()) { 34 | v.next(); 35 | printf(" (%d%%) ", v.progress()); 36 | } else { 37 | mm::log_status(MM_LOG_SILENT); 38 | 39 | timer::remove(h); 40 | v.free(); 41 | console::clear(); 42 | printf("Thank you for watching!\n"); 43 | printf("Repository can be found at https://github.com/foreverbell/BadAppleOS.\n"); 44 | timer::add(1, [](uint64_t, handle_t) -> void { 45 | systime_t *st = get_systime(NULL); 46 | printf("\rCurrent system time = %d/%02d/%02d %02d:%02d:%02d.", 47 | st->year, st->month, st->day, 48 | st->hour, st->minute, st->second); 49 | }); 50 | } 51 | }); 52 | } 53 | }); 54 | } 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BadAppleOS 2 | 3 | # Description 4 | 5 | Displaying [BadApple](https://www.youtube.com/watch?v=VzEUeWnV73U) animation in the protected mode of an x86 operating system. 6 | 7 | This operating system is quite rough - it doesn't have a file system, task scheduler and other more advanced things. 8 | 9 | So it is not an operating system indeed, just a program running on a bare computer LOL :) 10 | 11 | # Highlight 12 | 13 | * Except some necessary booting codes written in Assembly and C, other codes are purely written in C++(11). 14 | * A minimal STL port. 15 | * Artify plain texts to a great ASCII Art. 16 | * ... and this operating system is only 168K (depends on your compiler)! 17 | 18 | Notice you may see some weird designs in this OS, this is because our preliminary goal is to make it fit into a floppy. 19 | 20 | # Toolchains 21 | 22 | Tested under Ubuntu 22.04.2 LTS with the following toolchains, 23 | 24 | * gcc (11.3.0) 25 | * nasm (2.15.05) 26 | * python (3.9.17) 27 | 28 | I don't guarantee other versions of toolchains will work. 29 | 30 | # Build 31 | 32 | BadAppleOS is multi-boot compliant, so you can boot it with GRUB. We also provide a minimal bootloader without GRUB. 33 | 34 | ## No GRUB 35 | 36 | ```bash 37 | make img 38 | ``` 39 | 40 | Run `make qemu` to load the OS under QEMU. 41 | 42 | ## GRUB 43 | 44 | ```bash 45 | make docker 46 | make grub_iso 47 | ``` 48 | 49 | The docker step is sometimes necessary, because `grub-mkrescue` may not work properly under some systems. 50 | 51 | Run `make qemu_iso` to load the OS under QEMU. 52 | 53 | # License 54 | 55 | MIT 56 | -------------------------------------------------------------------------------- /boot/boot.asm: -------------------------------------------------------------------------------- 1 | [org 0x7c00] 2 | [bits 16] 3 | 4 | entry: 5 | ; jmp +0x4 6 | dw 0xeb04 7 | 8 | ; populated by link.py 9 | kernel_sectors dw 0 10 | 11 | ; clear cs 12 | jmp 0x0:start_16 13 | 14 | start_16: 15 | ; initialize segment registers & stack 16 | mov ax, cs 17 | mov ds, ax 18 | mov es, ax 19 | mov ss, ax 20 | mov bp, 0x8000 21 | mov sp, bp 22 | 23 | ; store boot drive 24 | mov [boot_drive], dl 25 | 26 | ; display the booting message 27 | mov si, booting_msg 28 | call print 29 | 30 | ; enable A20 line 31 | in al, 0x92 32 | or al, 0x2 33 | out 0x92, al 34 | 35 | ; detect floppy or disk 36 | call detect 37 | 38 | ; load kernel to 0x10000 39 | mov ax, [kernel_sectors] 40 | push 1 41 | push 0x1000 42 | push ax 43 | call load 44 | add sp, 6 45 | 46 | ; load gdt 47 | cli 48 | lgdt [gdt.descriptor] 49 | 50 | ; enable protection 51 | mov eax, cr0 52 | or eax, 0x1 53 | mov cr0, eax 54 | 55 | ; switch to protected mode 56 | jmp dword gdt.code:start_32 57 | 58 | [bits 32] 59 | 60 | start_32: 61 | ; initialize segment registers 62 | mov ax, gdt.data 63 | mov ds, ax 64 | mov es, ax 65 | mov fs, ax 66 | mov gs, ax 67 | mov ss, ax 68 | 69 | ; execute kernel, skip 0xc bytes for multi-boot header 70 | jmp 0x1000c 71 | 72 | [bits 16] 73 | 74 | disk_fatal: 75 | mov si, disk_fl_msg 76 | call print 77 | cli 78 | hlt 79 | 80 | %include "print.asm" 81 | %include "disk.asm" 82 | %include "gdt.asm" 83 | 84 | boot_drive db 0 85 | booting_msg db "Booting.", 0 86 | disk_fl_msg db "Disk error.", 0 87 | 88 | times 510 - ($ - $$) db 0 89 | dw 0xaa55 90 | -------------------------------------------------------------------------------- /kernel/Makefile: -------------------------------------------------------------------------------- 1 | ASFLAGS := -f elf 2 | CFLAGS := -O2 -ffreestanding -fno-builtin -fno-pic -nostdinc -fno-exceptions -fleading-underscore -I./include -I./stl -Wall -Wextra 3 | CCFLAGS := -std=c++11 -fno-rtti $(CFLAGS) 4 | LDFLAGS := -n -T link.ld -m elf_i386 5 | OCFLAGS := -O binary 6 | 7 | AS := nasm 8 | CC := gcc 9 | LD := ld 10 | OC := objcopy 11 | 12 | AS_OBJECTS := \ 13 | $(patsubst %.asm, %.o, $(wildcard krnl/*.asm)) 14 | 15 | CC_OBJECTS := \ 16 | $(patsubst %.cc, %.o, init.cc) \ 17 | $(patsubst %.cc, %.o, $(wildcard ba/*.cc)) \ 18 | $(patsubst %.cc, %.o, $(wildcard krnl/*.cc)) \ 19 | $(patsubst %.cc, %.o, $(wildcard krnl/abi/*.cc)) \ 20 | $(patsubst %.cc, %.o, $(wildcard krnl/cpu/*.cc)) \ 21 | $(patsubst %.cc, %.o, $(wildcard mm/*.cc)) \ 22 | $(patsubst %.cc, %.o, $(wildcard util/*.cc)) 23 | 24 | all: kernel.bin 25 | 26 | build: 27 | mkdir -p ../build 28 | 29 | kernel.bin: build $(AS_OBJECTS) $(CC_OBJECTS) begin.o tmppgdir.o vdata.o 30 | $(LD) -o kernel.elf $(LDFLAGS) $(AS_OBJECTS) $(CC_OBJECTS) tmppgdir.o vdata.o 31 | $(OC) $(OCFLAGS) kernel.elf kernel.bin 32 | cp kernel.elf ../build/kernel.elf 33 | cp kernel.bin ../build/kernel.bin 34 | 35 | begin.o: begin.asm 36 | $(AS) -o begin.o $(ASFLAGS) begin.asm 37 | 38 | tmppgdir.o: tmppgdir.c 39 | $(CC) -m32 -c -o tmppgdir.o $(CFLAGS) tmppgdir.c 40 | 41 | vdata.bin: 42 | python ../resource/encode.py ../resource/video.txt vdata.bin 43 | 44 | vdata.o: vdata.bin 45 | objcopy -B i386 -I binary -O elf32-i386 vdata.bin vdata.o 46 | 47 | $(AS_OBJECTS): %.o : %.asm 48 | $(AS) -o $@ $(ASFLAGS) $< 49 | 50 | $(CC_OBJECTS): %.o : %.cc 51 | $(CC) -m32 -c -o $@ $(CCFLAGS) $< 52 | 53 | dump: 54 | ndisasm -b32 -oC0000000h kernel.bin > dump.txt 55 | 56 | clean: 57 | rm -f *.o **/*.o **/**/*.o 58 | rm -f vdata.bin kernel.bin kernel.elf 59 | 60 | .PHONY: all dump clean 61 | -------------------------------------------------------------------------------- /kernel/stl/pair: -------------------------------------------------------------------------------- 1 | #ifndef __PAIR__ 2 | #define __PAIR__ 3 | 4 | namespace std { 5 | 6 | template 7 | struct pair { 8 | typedef type_1 first_type; 9 | typedef type_2 second_type; 10 | 11 | type_1 first; 12 | type_2 second; 13 | 14 | pair() : first(), second() { } 15 | 16 | pair(const type_1 &a, const type_2 &b) : first(a), second(b) { } 17 | 18 | template 19 | pair(const pair &p) : first(p.first), second(p.second) { } 20 | }; 21 | 22 | template 23 | inline bool operator == (const pair &x, const pair &y) { 24 | return x.first == y.first && x.second == y.second; 25 | } 26 | 27 | template 28 | inline bool operator < (const pair &x, const pair &y) { 29 | return x.first < y.first || (!(y.first < x.first) && x.second < y.second); 30 | } 31 | 32 | template 33 | inline bool operator != (const pair &x, const pair &y) { 34 | return !(x == y); 35 | } 36 | 37 | template 38 | inline bool operator > (const pair &x, const pair &y) { 39 | return y < x; 40 | } 41 | 42 | template 43 | inline bool operator <= (const pair &x, const pair &y) { 44 | return !(y < x); 45 | } 46 | 47 | template 48 | inline bool operator >= (const pair &x, const pair &y) { 49 | return !(x < y); 50 | } 51 | 52 | template 53 | inline pair make_pair(type_1 x, type_2 y) { 54 | return pair(x, y); 55 | } 56 | 57 | } 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /kernel/krnl/gdt.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | namespace gdt { 7 | 8 | namespace { 9 | 10 | #define MAX_GDT_ENTRY 256 11 | 12 | #pragma pack(push, 1) 13 | struct gdt_entry_t { 14 | uint16_t limit; 15 | uint16_t base_low; 16 | uint8_t base_middle; 17 | uint8_t access; 18 | uint8_t granularity; 19 | uint8_t base_high; 20 | } __attribute__((packed)); 21 | 22 | struct gdt_descriptior_t { 23 | uint16_t limit; 24 | uint32_t base; 25 | } __attribute__((packed)); 26 | #pragma pack(pop) 27 | 28 | gdt_entry_t gdt[MAX_GDT_ENTRY]; 29 | gdt_descriptior_t gdt_ptr; 30 | 31 | void flush(void) { 32 | /* initialize gdt ptr. */ 33 | gdt_ptr.limit = sizeof(gdt) - 1; 34 | gdt_ptr.base = (uint32_t) gdt; 35 | 36 | __asm__ __volatile__ ( 37 | "lgdt %0\n\t" 38 | "ljmp $0x8, $1f\n\t" /* far jump to the code segment. */ 39 | "1:\n\t" 40 | "movw $0x10, %%ax\n\t" /* set data segment. */ 41 | "movw %%ax, %%ds\n\t" 42 | "movw %%ax, %%ss\n\t" 43 | "movw %%ax, %%es\n\t" 44 | "movw %%ax, %%fs\n\t" 45 | "movw %%ax, %%gs\n\t" 46 | :"=m"(gdt_ptr) 47 | ); 48 | } 49 | 50 | } 51 | 52 | void set_gate(int index, uint32_t base, uint32_t limit, uint8_t access, uint8_t granularity) { 53 | gdt[index].base_low = base & 0xffff; 54 | gdt[index].base_middle = (base >> 16) & 0xff; 55 | gdt[index].base_high = (base >> 24) & 0xff; 56 | gdt[index].limit = limit; 57 | gdt[index].access = access; 58 | gdt[index].granularity = granularity; 59 | } 60 | 61 | void initialize(void) { 62 | /* initialize all gdt entries. */ 63 | memset(gdt, 0, sizeof(gdt)); 64 | 65 | /* setup gdt gates. */ 66 | set_gate(0, 0, 0x0, 0x0, 0x0); // null gdt entry 67 | set_gate(1, 0, 0xffff, 0x9a, 0xcf); // code segment 68 | set_gate(2, 0, 0xffff, 0x92, 0xcf); // data segment 69 | 70 | /* flush gdt */ 71 | flush(); 72 | } 73 | 74 | } /* gdt */ 75 | -------------------------------------------------------------------------------- /kernel/krnl/cpu/manipulate.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace cpu { 9 | 10 | namespace manipulate { 11 | 12 | namespace { 13 | 14 | int cli_count = 1; 15 | bool int_aon = true; // true if interrupt if already enabled 16 | 17 | #define FLAGS_IF 0x200 18 | 19 | uint32_t readeflags(void) { 20 | uint32_t eflags; 21 | __asm__ __volatile__ ("pushfl; popl %0" : "=r" (eflags)); 22 | return eflags; 23 | } 24 | 25 | } 26 | 27 | void cli(void) { 28 | uint32_t eflags = readeflags(); 29 | __asm__ __volatile__ ("cli"); 30 | if (cli_count == 0) { 31 | int_aon = eflags & FLAGS_IF; 32 | } 33 | cli_count += 1; 34 | } 35 | 36 | void sti(void) { 37 | cli_count -= 1; 38 | assert(cli_count >= 0); 39 | if (cli_count == 0 && int_aon) { 40 | int_aon = false; 41 | __asm__ __volatile__ ("sti"); 42 | } 43 | } 44 | 45 | void halt(void) { 46 | __asm__ __volatile__ ("hlt"); 47 | } 48 | 49 | /* clear interrupts and halt. */ 50 | void die(void) { 51 | cli(); 52 | halt(); 53 | } 54 | 55 | void sleep(uint64_t ticks) { 56 | uint64_t till = ticks + timer::get_system_tick(); 57 | while (timer::get_system_tick() < till) { 58 | halt(); 59 | } 60 | } 61 | 62 | /* impl of int_guard. */ 63 | int_guard::int_guard() : cleared(false) { 64 | acquire(); 65 | } 66 | 67 | int_guard::~int_guard() { 68 | release(); 69 | } 70 | 71 | void int_guard::acquire(void) { 72 | if (cleared) { 73 | panic::panic("[int_guard::acquire] Already acquired."); 74 | } 75 | cli(); 76 | cleared = true; 77 | } 78 | 79 | void int_guard::release(void) { 80 | if (!cleared) { 81 | panic::panic("[int_guard::release] Nothing to release."); 82 | } 83 | cleared = false; 84 | sti(); 85 | } 86 | 87 | bool int_guard::owns(void) const { 88 | return cleared; 89 | } 90 | 91 | } /* manipulate */ 92 | 93 | } /* cpu */ 94 | -------------------------------------------------------------------------------- /kernel/krnl/systime.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | using cmos::read; 7 | 8 | namespace systime { 9 | 10 | namespace { 11 | 12 | systime_t systime; 13 | 14 | void try_get(systime_t *in_ptr) { 15 | /* check if CMOS is updating data. */ 16 | while (read(0xa) & 0x80) { 17 | __asm__ __volatile__ ("nop"); 18 | } 19 | in_ptr->second = read(0x0); 20 | in_ptr->minute = read(0x2); 21 | in_ptr->hour = read(0x4); 22 | in_ptr->day = read(0x7); 23 | in_ptr->month = read(0x8); 24 | in_ptr->year = read(0x9); 25 | } 26 | 27 | } 28 | 29 | systime_t *get_systime(systime_t *in_ptr) { 30 | systime_t cur_systime; 31 | 32 | for (try_get(&systime); ; ) { 33 | try_get(&cur_systime); 34 | if (cur_systime == systime) { 35 | break; 36 | } 37 | systime = cur_systime; 38 | } 39 | 40 | /* read status register B. */ 41 | int status_B = read(0xb); 42 | 43 | /* read and clear 0x80 bit of hour. */ 44 | int hour_h8 = systime.hour & 0x80; 45 | systime.hour &= ~0x80; 46 | 47 | if (~status_B & 0x4) { 48 | /* detected BCD mode, convert to binary mode. */ 49 | #define BCD_TO_BINARY(x) (((x) & 0xf) + (((x) >> 4) * 10)) 50 | systime.second = BCD_TO_BINARY(systime.second); 51 | systime.minute = BCD_TO_BINARY(systime.minute); 52 | systime.hour = BCD_TO_BINARY(systime.hour); 53 | systime.day = BCD_TO_BINARY(systime.day); 54 | systime.month = BCD_TO_BINARY(systime.month); 55 | systime.year = BCD_TO_BINARY(systime.year); 56 | #undef BCD_TO_BINARY 57 | } 58 | 59 | /* convert 12 hour to 24 hour if necessary. */ 60 | if ((~status_B & 0x2) && hour_h8) { 61 | systime.hour = (systime.hour + 12) % 24; 62 | } 63 | 64 | /* 2-digit to full 4-digit. */ 65 | systime.year += 2000; // I don't think my OS can live that far to 21xx. 66 | 67 | if (in_ptr != NULL) { 68 | in_ptr->second = systime.second; 69 | in_ptr->minute = systime.minute; 70 | in_ptr->hour = systime.hour; 71 | in_ptr->day = systime.day; 72 | in_ptr->month = systime.month; 73 | in_ptr->year = systime.year; 74 | 75 | return in_ptr; 76 | } 77 | 78 | return &systime; 79 | } 80 | 81 | } /* systime */ 82 | -------------------------------------------------------------------------------- /resource/encode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | from heapq import * 6 | 7 | 8 | class hnode(object): 9 | def __init__(self, freq, val=None, left=None, right=None): 10 | self.freq = freq 11 | self.val = val 12 | self.left = left 13 | self.right = right 14 | 15 | def is_leaf(self): 16 | return self.val is not None 17 | 18 | def __lt__(self, r): 19 | return self.freq < r.freq 20 | 21 | 22 | def build(freq): 23 | queue = [] 24 | for i in range(0, 256): 25 | if freq[i] != 0: 26 | heappush(queue, hnode(freq[i], val=i)) 27 | while len(queue) > 1: 28 | a = heappop(queue) 29 | b = heappop(queue) 30 | heappush(queue, hnode(a.freq + b.freq, left=a, right=b)) 31 | return queue[0] 32 | 33 | 34 | def walk(node, prefix="", code={}): 35 | if node.is_leaf(): 36 | code[node.val] = prefix 37 | else: 38 | walk(node.left, prefix + "0", code) 39 | walk(node.right, prefix + "1", code) 40 | return code 41 | 42 | 43 | def huffman(lst): 44 | freq = [0] * 256 45 | for n in lst: 46 | freq[n] += 1 47 | return walk(build(freq)) 48 | 49 | 50 | def encode(s, ch): 51 | while len(s) % 8 != 0: 52 | s += ch 53 | lst = [] 54 | for i in range(0, len(s) // 8): 55 | n = 0 56 | for j in range(0, 8): 57 | k = i * 8 + j 58 | n |= (1 if (s[k] == ch) else 0) << j 59 | lst.append(n) 60 | return lst 61 | 62 | 63 | def compress(content): 64 | fcount = len(content) // 80 // 25 # frame_count 65 | edata = encode(content, '*') 66 | elen = len(edata) 67 | code = huffman(edata) 68 | hdata = "" 69 | for item in edata: 70 | hdata += code[item] 71 | code_bytes = [ # reverse the endian 72 | fcount >> 8, fcount & 0xff, elen >> 24, (elen >> 16) & 0xff, 73 | (elen >> 8) & 0xff, elen & 0xff, 74 | len(code) 75 | ] 76 | for key, value in code.items(): 77 | code_bytes.append(key) 78 | tmp = encode(value, '1') 79 | code_bytes.append(len(value)) 80 | code_bytes += tmp 81 | return code_bytes + encode(hdata, '1') 82 | 83 | 84 | if __name__ == "__main__": 85 | txt, bin = sys.argv[1], sys.argv[2] 86 | data = [] 87 | 88 | with open(txt, "r") as fin: 89 | with open(bin, "wb") as fout: 90 | content = ''.join(fin.readlines()) 91 | content = ''.join(content.splitlines()) 92 | fout.write(bytearray(compress(content))) 93 | -------------------------------------------------------------------------------- /kernel/stl/algorithm: -------------------------------------------------------------------------------- 1 | #ifndef __ALGORITHM__ 2 | #define __ALGORITHM__ 3 | 4 | #include 5 | #include 6 | 7 | namespace std { 8 | 9 | template 10 | inline void swap(value_type &a, value_type &b) { 11 | value_type t = a; a = b; b = t; 12 | } 13 | 14 | template 15 | inline void _shiftd_heap(random_iterator root, random_iterator first, random_iterator last, compare comp) { 16 | for (random_iterator cur = first, lch = (first - root) + first + 1; lch < last; ) { 17 | random_iterator rch, to_swap = cur; 18 | 19 | if (comp(*to_swap, *lch)) { 20 | to_swap = lch; 21 | } 22 | rch = lch + 1; 23 | if (rch < last && comp(*to_swap, *rch)) { 24 | to_swap = rch; 25 | } 26 | if (to_swap == cur) { 27 | break; 28 | } else { 29 | swap(*cur, *to_swap); 30 | cur = to_swap; 31 | lch = (cur - root) + cur + 1; 32 | } 33 | } 34 | } 35 | 36 | template 37 | inline void make_heap(random_iterator first, random_iterator last, compare comp) { 38 | random_iterator cur = first + (last - first - 2) / 2; 39 | while (cur >= first) { 40 | _shiftd_heap(first, cur--, last, comp); 41 | } 42 | } 43 | 44 | template 45 | inline void make_heap(random_iterator first, random_iterator last) { 46 | make_heap(first, last, less::value_type>()); 47 | } 48 | 49 | template 50 | inline void pop_heap(random_iterator first, random_iterator last, compare comp) { 51 | swap(*first, *(last - 1)); 52 | _shiftd_heap(first, first, last - 1, comp); 53 | } 54 | 55 | template 56 | inline void pop_heap(random_iterator first, random_iterator last) { 57 | pop_heap(first, last, less::value_type>()); 58 | } 59 | 60 | template 61 | inline void sort(random_iterator first, random_iterator last, compare comp) { 62 | make_heap(first, last, comp); 63 | while (first < last) { 64 | pop_heap(first, last, comp); 65 | last -= 1; 66 | } 67 | } 68 | 69 | template 70 | inline void sort(random_iterator first, random_iterator last) { 71 | sort(first, last, less::value_type>()); 72 | } 73 | 74 | } /* std */ 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /kernel/mm/init.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace mm { 9 | 10 | #define NVRAM_BASELO 0x15 11 | #define NVRAM_BASEHI 0x16 12 | #define NVRAM_EXTLO 0x17 13 | #define NVRAM_EXTHI 0x18 14 | #define NVRAM_EXT16LO 0x34 15 | #define NVRAM_EXT16HI 0x35 16 | 17 | #define PAGE_PRESENT (1 << 0) 18 | #define PAGE_WRITE (1 << 1) 19 | 20 | // To be simple, we use physical memory of [0x1000, 0x6000) for PDE and PTE. 21 | // PDE and each PTE occupy one page. 22 | 23 | #define PDE 0x1000 24 | #define PTE1 0x2000 25 | #define PTE2 0x3000 26 | #define PTE3 0x4000 27 | #define PTE4 0x5000 28 | 29 | namespace { 30 | 31 | uint32_t * const pde_addr = (uint32_t *) PDE; 32 | 33 | /* map VA `index*4M+4K*[from, to)` to PA `phys+4K*[from, to)`. */ 34 | void setup_pte(int index, uint32_t pte_addr, uint32_t phys, int from, int to) { 35 | pde_addr[index] = pte_addr | PAGE_PRESENT | PAGE_WRITE; 36 | uint32_t *p = (uint32_t *) pte_addr; 37 | 38 | for (int i = 0; i < 1024; ++i) { 39 | p[i] = 0; 40 | if (i >= from && i < to) { 41 | p[i] = ((i << 12) + phys) | PAGE_PRESENT | PAGE_WRITE; 42 | } 43 | } 44 | } 45 | 46 | } 47 | 48 | void initialize(void) { 49 | auto read = [](int r) -> size_t { return cmos::read(r); }; 50 | size_t basemem, extmem, ext16mem, totalmem; 51 | 52 | /* use CMOS calls to measure available base & extended memory. */ 53 | /* measured in kilobytes. */ 54 | basemem = read(NVRAM_BASELO) | (read(NVRAM_BASEHI) << 8); 55 | extmem = read(NVRAM_EXTLO) | (read(NVRAM_EXTHI) << 8); 56 | ext16mem = (read(NVRAM_EXT16LO) | (read(NVRAM_EXT16HI) << 8)) << 6; 57 | 58 | if (ext16mem) { 59 | totalmem = 16 * 1024 + ext16mem; 60 | } else if (extmem) { 61 | totalmem = 1 * 1024 + extmem; 62 | } else { 63 | totalmem = basemem; 64 | } 65 | 66 | printf("[mm] Physical total = %uK, base = %uK, extended = %uK.\n", 67 | totalmem, basemem, totalmem - basemem); 68 | 69 | /* reinitialize PDE and PTE. */ 70 | 71 | /* clear all PDE entries. */ 72 | for (int i = 0; i < 1024; ++i) { 73 | pde_addr[i] = 0; 74 | } 75 | 76 | /* set lower 1M memory as identity paging. */ 77 | setup_pte(0, PTE1, 0x0, 0, 256); 78 | 79 | /* 0xc0000000 ~ 0xc00f0000 for kernel (physical 0x0 ~ 0xf0000), 960K. */ 80 | setup_pte(768, PTE2, 0x0, 0, 240); 81 | 82 | /* leave a memory hole here as guard pages. */ 83 | 84 | /* 0xc0400000 ~ 0xc0c00000 for memory pool (physical 0x100000 ~ 0x900000), 8M. */ 85 | setup_pte(769, PTE3, 0x100000, 0, 1024); 86 | setup_pte(770, PTE4, 0x500000, 0, 1024); 87 | 88 | /* reload the new page directory. */ 89 | __asm__ __volatile__ ("movl %0, %%cr3" : : "r" (pde_addr)); 90 | } 91 | 92 | } /* mm */ 93 | -------------------------------------------------------------------------------- /boot/disk.asm: -------------------------------------------------------------------------------- 1 | ; Refer to http://wiki.osdev.org/ATA_in_x86_RealMode_(BIOS) for full 2 | ; documentation. 3 | 4 | ; @function: detect 5 | ; @brief: detect drive parameters via int 13h, ah=8h. 6 | ; @parameters: none. 7 | detect: 8 | pusha 9 | xor ax, ax 10 | mov es, ax 11 | mov di, ax 12 | mov ah, 0x8 13 | mov dl, [boot_drive] 14 | int 0x13 15 | jnc .ok 16 | popa 17 | ret ; use default floopy 18 | 19 | .ok: 20 | mov dl, dh 21 | xor dh, dh 22 | inc dx 23 | mov [total_heads], dx 24 | mov ax, cx 25 | and ax, 0x3f 26 | mov [sectors_per_track], ax 27 | popa 28 | ret 29 | 30 | ; @function: load 31 | ; @brief: load the kernel image to memory. 32 | ; @parameters: start sector, destination, the number of sectors to read. 33 | load: 34 | push bp 35 | mov bp, sp 36 | pusha 37 | 38 | mov ax, [bp + 4] ; sectors to read 39 | xor bx, bx 40 | mov dx, [bp + 6] ; load to where? 41 | mov cx, [bp + 8] ; start sector 42 | 43 | .loop: 44 | cmp ax, 0 45 | je .ok 46 | push cx 47 | push dx 48 | push bx 49 | call read 50 | add sp, 6 51 | dec ax 52 | add bx, 0x200 53 | inc cx 54 | cmp bx, 0x1000 55 | jne .loop 56 | add dx, 0x100 57 | xor bx, bx 58 | jmp .loop 59 | 60 | .ok: 61 | popa 62 | pop bp 63 | ret 64 | 65 | ; @function: read 66 | ; @brief: read a sector from drive. 67 | ; @parameters: buffer offset, buffer segment, LBA. 68 | read: 69 | push bp 70 | mov bp, sp 71 | pusha 72 | 73 | mov ax, [bp + 8] 74 | xor dx, dx 75 | mov bx, [sectors_per_track] 76 | div bx 77 | inc dx ; sector is 1-based 78 | mov [sector_index], dx 79 | xor dx, dx 80 | mov bx, [total_heads] 81 | div bx 82 | mov [head_index], dx 83 | mov [cylinder_index], ax 84 | 85 | mov [number_retries], byte 0 86 | 87 | ; read a sector 88 | mov bx, [cylinder_index] 89 | mov ch, bl 90 | mov bx, [sector_index] 91 | mov cl, bl 92 | mov bx, [cylinder_index] 93 | shr bx, 2 94 | and bx, 0xc0 95 | or cl, bl ; not necessary for floppy 96 | mov bx, [head_index] 97 | mov dh, bl 98 | mov bx, [bp + 4] 99 | mov es, [bp + 6] 100 | mov dl, [boot_drive] 101 | 102 | .loop: 103 | mov ah, 0x2 104 | mov al, 0x1 105 | int 0x13 106 | jnc .ok 107 | 108 | ; reset disk drive 109 | xor ax, ax 110 | int 0x13 111 | 112 | inc byte [number_retries] 113 | cmp byte [number_retries], 5 ; retry 5 times 114 | 115 | jne .loop 116 | jmp disk_fatal ; see boot.asm 117 | 118 | .ok: 119 | popa 120 | pop bp 121 | ret 122 | 123 | sector_index dw 0 124 | head_index dw 0 125 | cylinder_index dw 0 126 | number_retries db 0 127 | 128 | ; floppy default value (place holder, can be overwritten) 129 | sectors_per_track dw 18 130 | total_heads dw 2 131 | -------------------------------------------------------------------------------- /kernel/ba/decompressor.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "badapple.h" 7 | 8 | namespace badapple { 9 | 10 | namespace btrie { 11 | 12 | struct node_t { 13 | node_t *child[2]; 14 | int label; 15 | } root; 16 | 17 | void insert(node_t *p, const int8_t *pdat, int label) { 18 | while (*pdat != -1) { 19 | int data = *pdat; 20 | if (p->child[data] == NULL) { 21 | p->child[data] = new node_t(); 22 | memset(p->child[data], 0, sizeof(node_t)); 23 | p->child[data]->label = -1; 24 | } 25 | p = p->child[data]; 26 | pdat += 1; 27 | } 28 | p->label = label; 29 | } 30 | 31 | void free(node_t *p) { 32 | for (int i = 0; i < 2; ++i) { 33 | if (p->child[i] != NULL) { 34 | free(p->child[i]); 35 | } 36 | } 37 | if (p != &root) { 38 | delete [] p; 39 | } 40 | } 41 | 42 | } /* btrie */ 43 | 44 | decompressor::decompressor(const uint8_t *vdatas, const uint8_t *vdatae) { 45 | auto read = [&](const uint8_t *data, int bytes) -> int { 46 | int ret = 0; 47 | while (bytes-- > 0) { 48 | ret = (ret << 8) | *data++; 49 | } 50 | return ret; 51 | }; 52 | int buffer_size, key_count; 53 | 54 | count = read(vdatas, 2); // frame_count 55 | buffer_size = read(vdatas + 2, 4); 56 | key_count = read(vdatas + 6, 1); 57 | 58 | printf("[decompressor] Frame count = %d.\n", count); 59 | printf("[decompressor] Buffer size = %d.\n", buffer_size); 60 | printf("[decompressor] Key count = %d.\n", key_count); 61 | 62 | buffer = new uint8_t[buffer_size]; 63 | buffer_end = buffer + buffer_size; 64 | 65 | const uint8_t *pointer = vdatas + 7; 66 | 67 | mm::log_status(MM_LOG_SILENT); { // tell memory logger to shut up 68 | 69 | for (int i = 0; i < key_count; ++i) { 70 | int key = (int) *pointer++; 71 | int length = (int) *pointer++; 72 | static int8_t data[256]; 73 | data[length] = -1; 74 | stream reader = stream(pointer, pointer + (length - 1) / 8 + 1); 75 | for (int i = 0; i < length; ++i) { 76 | data[i] = reader.nextb(); 77 | } 78 | btrie::insert(&btrie::root, data, key); 79 | pointer += (length - 1) / 8 + 1; 80 | } 81 | 82 | btrie::node_t *cur = &btrie::root; 83 | stream reader = stream(pointer, vdatae); 84 | for (int i = 0; i < buffer_size; ++i) { 85 | while (true) { 86 | int bit = reader.nextb(); 87 | cur = cur->child[bit]; 88 | if (cur->label != -1) { 89 | buffer[i] = cur->label; 90 | cur = &btrie::root; 91 | break; 92 | } 93 | } 94 | } 95 | 96 | btrie::free(&btrie::root); 97 | 98 | printf("[decompressor] Remaining %d bits.\n", reader.remain()); 99 | 100 | } mm::log_status(MM_LOG_NOISY); 101 | } 102 | 103 | decompressor::~decompressor() { 104 | delete [] buffer; 105 | } 106 | 107 | } /* badapple */ 108 | -------------------------------------------------------------------------------- /kernel/krnl/timer.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using cpu::manipulate::int_guard; 12 | 13 | namespace timer { 14 | 15 | namespace { 16 | 17 | #define PORT_PIT_CHANNEL0 0x40 18 | #define PORT_PIT_CHANNEL1 0x41 19 | #define PORT_PIT_CHANNEL2 0x42 20 | #define PORT_PIT_CMD 0x43 21 | 22 | struct timer_callback_node_t { 23 | /* all measured in tick, not second. */ 24 | uint64_t interval, trigger_count, count_down; 25 | timer_callback_node_t *prev, *next; 26 | fn_timer_callback_t lpfn_timer_callback; 27 | bool deleted; 28 | } *timer_head; 29 | 30 | uint64_t system_tick; 31 | 32 | void handler(irq::irq_context_t * /*context_ptr*/) { 33 | system_tick += 1; 34 | 35 | timer_callback_node_t *cur = timer_head; 36 | 37 | while (cur != NULL) { 38 | cur->count_down -= 1; 39 | 40 | bool should_remove = cur->deleted; 41 | bool should_call = !cur->deleted && cur->count_down == 0; 42 | 43 | if (should_call) { 44 | cur->lpfn_timer_callback(cur->trigger_count, (handle_t) cur); 45 | cur->trigger_count += 1; 46 | cur->count_down = cur->interval; 47 | } 48 | if (should_remove) { 49 | if (timer_head == cur) { 50 | timer_head = cur->next; 51 | } 52 | if (cur->prev != NULL) { 53 | cur->prev->next = cur->next; 54 | } 55 | if (cur->next != NULL) { 56 | cur->next->prev = cur->prev; 57 | } 58 | timer_callback_node_t *next = cur->next; 59 | delete cur; 60 | cur = next; 61 | } else { 62 | cur = cur->next; 63 | } 64 | } 65 | } 66 | 67 | } 68 | 69 | void initialize(void) { 70 | system_tick = 0; 71 | 72 | /* clear timer link head. */ 73 | timer_head = NULL; 74 | 75 | /* default tick rate, 18 ticks = 1 second. */ 76 | port::outb(PORT_PIT_CMD, 0x36); 77 | port::outb(PORT_PIT_CHANNEL0, 0); 78 | port::outb(PORT_PIT_CHANNEL0, 0); 79 | 80 | /* install and enable coresponding IRQ. */ 81 | irq::install(0, handler); 82 | irq::enable(0); 83 | } 84 | 85 | uint64_t get_system_tick(void) { 86 | return system_tick; 87 | } 88 | 89 | handle_t add(uint64_t interval, fn_timer_callback_t lpfn_callback) { 90 | if (interval == 0) { 91 | return TIMER_INVALID_HANDLE; 92 | } 93 | 94 | int_guard guard; 95 | timer_callback_node_t *ptr = new timer_callback_node_t(); 96 | 97 | if (ptr == NULL) { 98 | return TIMER_INVALID_HANDLE; 99 | } 100 | 101 | ptr->deleted = false; 102 | ptr->prev = NULL; 103 | ptr->next = timer_head; 104 | if (timer_head != NULL) { 105 | timer_head->prev = ptr; 106 | } 107 | ptr->interval = interval; 108 | ptr->trigger_count = 0; 109 | ptr->count_down = interval; 110 | ptr->lpfn_timer_callback = lpfn_callback; 111 | timer_head = ptr; 112 | 113 | return (handle_t) ptr; 114 | } 115 | 116 | bool remove(handle_t ptr) { 117 | int_guard guard; 118 | timer_callback_node_t *timer_ptr = (timer_callback_node_t *) ptr; 119 | 120 | if (timer_ptr->deleted) { 121 | return false; 122 | } 123 | timer_ptr->deleted = true; 124 | return true; 125 | } 126 | 127 | } /* timer */ 128 | -------------------------------------------------------------------------------- /kernel/krnl/isr.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | asmlinkage { 9 | 10 | void isr_handler0(); 11 | void isr_handler1(); 12 | void isr_handler2(); 13 | void isr_handler3(); 14 | void isr_handler4(); 15 | void isr_handler5(); 16 | void isr_handler6(); 17 | void isr_handler7(); 18 | void isr_handler8(); 19 | void isr_handler9(); 20 | void isr_handler10(); 21 | void isr_handler11(); 22 | void isr_handler12(); 23 | void isr_handler13(); 24 | void isr_handler14(); 25 | void isr_handler15(); 26 | void isr_handler16(); 27 | void isr_handler17(); 28 | void isr_handler18(); 29 | 30 | } 31 | 32 | namespace isr { 33 | 34 | namespace { 35 | 36 | const char *exception_message[] = { 37 | "Division By Zero Exception", // fault 38 | "Debug Exception", // fault/trap 39 | "Non Maskable Interrupt Exception", // interrupt 40 | "Breakpoint Exception", // trap 41 | "Into Detected Overflow Exception", // trap 42 | "Out of Bounds Exception", // fault 43 | "Invalid Opcode Exception", // fault 44 | "No Coprocessor Exception", // fault 45 | "Double Fault Exception", // abort 46 | "Coprocessor Segment Overrun Exception", // fault 47 | "Bad TSS Exception", // fault 48 | "Segment Not Present Exception", // fault 49 | "Stack Fault Exception", // fault 50 | "General Protection Fault Exception", // fault 51 | "Page Fault Exception", // fault 52 | "Unknown Interrupt Exception", 53 | "Coprocessor Fault Exception", // fault 54 | "Alignment Check Exception", // fault 55 | "Machine Check Exception" // abort 56 | }; 57 | 58 | #pragma pack(push, 1) 59 | struct isr_context_t { 60 | uint32_t gs, fs, es, ds; 61 | uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; 62 | uint32_t isr_index, error_code; 63 | uint32_t eip, cs, eflags; 64 | } __attribute__((packed)); 65 | #pragma pack(pop) 66 | 67 | void dispatcher(isr_context_t *ptr) { 68 | printf("Exception = %s, with error code = %d.\n", 69 | exception_message[ptr->isr_index], ptr->error_code); 70 | printf("Registers:\n"); 71 | printf("\tds 0x%08x\tes 0x%08x\tfs 0x%08x\tgs 0x%08x\n", 72 | ptr->ds, ptr->es, ptr->fs, ptr->gs); 73 | printf("\teax 0x%08x\tebx 0x%08x\tecx 0x%08x\tedx 0x%08x\n", 74 | ptr->eax, ptr->ebx, ptr->ecx, ptr->edx); 75 | printf("\tesp 0x%08x\tebp 0x%08x\tesi 0x%08x\tedi 0x%08x\n", 76 | ptr->esp, ptr->ebp, ptr->esi, ptr->edi); 77 | printf("\teip 0x%08x\tcs 0x%08x\teflags 0x%08x\n", 78 | ptr->eip, ptr->cs, ptr->eflags); 79 | 80 | printf("System halted.\n"); 81 | cpu::manipulate::die(); 82 | } 83 | 84 | } 85 | 86 | void initialize(void) { 87 | #define set_isr(n) idt::set_gate(n, (uint32_t) isr_handler##n, KERNEL_CODE_SEL, 0x8e); 88 | set_isr(0); 89 | set_isr(1); 90 | set_isr(2); 91 | set_isr(3); 92 | set_isr(4); 93 | set_isr(5); 94 | set_isr(6); 95 | set_isr(7); 96 | set_isr(8); 97 | set_isr(9); 98 | set_isr(10); 99 | set_isr(11); 100 | set_isr(12); 101 | set_isr(13); 102 | set_isr(14); 103 | set_isr(15); 104 | set_isr(16); 105 | set_isr(17); 106 | set_isr(18); 107 | #undef set_isr 108 | } 109 | 110 | } /* isr */ 111 | 112 | asmlinkage void isr_dispatcher(isr::isr_context_t *ptr) { 113 | isr::dispatcher(ptr); 114 | } 115 | -------------------------------------------------------------------------------- /kernel/krnl/abi/dtor.cc: -------------------------------------------------------------------------------- 1 | /* warning: this code is not carefully tested. */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using std::vector; 12 | using std::pair; 13 | using std::make_pair; 14 | using std::sort; 15 | 16 | typedef void (* fn_ptr) (void *); 17 | typedef pair atexit_info_t; // pobj, insert_index 18 | 19 | namespace abi { 20 | 21 | namespace { 22 | 23 | struct atexit_t { 24 | fn_ptr lpfn; 25 | vector atexits; 26 | atexit_t *next; 27 | }; 28 | 29 | #define SLOT_SIZE 131 // prime 30 | 31 | atexit_t *slot_header[SLOT_SIZE]; 32 | size_t cur_insert_index; 33 | 34 | uint32_t hash(fn_ptr lpfn) { 35 | return uint32_t(lpfn) * uint32_t(2654435761); 36 | } 37 | 38 | /* we don't need dso_handle actually. */ 39 | int cxa_atexit(fn_ptr lpfn, void *pobj, void * /* dso_handle */) { 40 | printf("[cxa_atexit] Register lpfn = 0x%x, pobj = 0x%x\n", lpfn, pobj); 41 | 42 | uint32_t h = hash(lpfn) % SLOT_SIZE; 43 | atexit_t *patexit = slot_header[h]; 44 | 45 | while (patexit != NULL) { 46 | if (patexit->lpfn == lpfn) { 47 | break; 48 | } else { 49 | patexit = patexit->next; 50 | } 51 | } 52 | 53 | if (patexit == NULL) { 54 | patexit = new atexit_t(); 55 | patexit->lpfn = lpfn; 56 | patexit->next = slot_header[h]; 57 | slot_header[h] = patexit; 58 | } 59 | 60 | patexit->atexits.push_back(make_pair(pobj, ++cur_insert_index)); 61 | 62 | return 0; 63 | } 64 | 65 | void cxa_finalize(fn_ptr lpfn) { 66 | if (lpfn != NULL) { 67 | uint32_t h = hash(lpfn) % SLOT_SIZE; 68 | atexit_t *patexit = slot_header[h], *prev = NULL; 69 | 70 | while (patexit != NULL) { 71 | if (patexit->lpfn == lpfn) { 72 | break; 73 | } else { 74 | prev = patexit; 75 | patexit = patexit->next; 76 | } 77 | } 78 | if (patexit == NULL) { 79 | /* key lpfn is not found in the table .*/ 80 | panic::panic("[cxa_finalize] Bad function pointer."); 81 | } 82 | for (int i = int(patexit->atexits.size()) - 1; i >= 0; --i) { 83 | patexit->lpfn(patexit->atexits[i].first); 84 | } 85 | if (prev == NULL) { 86 | slot_header[h] = patexit->next; 87 | } else { 88 | prev->next = patexit->next; 89 | } 90 | delete patexit; 91 | } else { 92 | vector> all; 93 | for (int i = 0; i < SLOT_SIZE; ++i) { 94 | atexit_t *p = slot_header[i]; 95 | while (p != NULL) { 96 | for (auto &item : p->atexits) { 97 | all.push_back(make_pair(p->lpfn, item)); 98 | } 99 | auto backup = p; 100 | p = p->next; 101 | delete backup; 102 | } 103 | } 104 | sort(all.begin(), all.end(), [&](const pair &a, const pair &b) -> bool { 105 | return a.second.second > b.second.second; // compared by insert order 106 | }); 107 | for (auto it = all.begin(); it != all.end(); ++it) { 108 | it->first(it->second.first); 109 | } 110 | } 111 | } 112 | 113 | } 114 | 115 | } /* abi */ 116 | 117 | /* exports go here. */ 118 | 119 | #ifdef __cplusplus 120 | extern "C" { 121 | #endif /* __cplusplus */ 122 | 123 | void *__dso_handle = 0; 124 | 125 | int __cxa_atexit(fn_ptr lpfn, void *pobj, void *dso_handle) { 126 | return abi::cxa_atexit(lpfn, pobj, dso_handle); 127 | } 128 | 129 | void __cxa_finalize(fn_ptr lpfn) { 130 | abi::cxa_finalize(lpfn); 131 | } 132 | 133 | #ifdef __cplusplus 134 | } 135 | #endif /* __cplusplus */ 136 | 137 | namespace abi { 138 | 139 | void dtors(void) { 140 | __cxa_finalize(NULL); 141 | } 142 | 143 | } /* abi */ 144 | -------------------------------------------------------------------------------- /kernel/krnl/console.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace console { 9 | 10 | /* 11 | * ------------------> y (80) 12 | * | > _ | 13 | * | console | 14 | * | | 15 | * x-----------------x 16 | * (25) 17 | */ 18 | 19 | namespace { 20 | 21 | int attrib; 22 | 23 | namespace cursor { 24 | 25 | int X, Y; 26 | bool show_cursor; 27 | int vport; // base IO port for video, usually 0x3d4 28 | 29 | void move(int x, int y) { 30 | int offset = x * VIDEO_MAX_COLUMN + y; 31 | 32 | port::outb(vport, 14); 33 | port::outb(vport | 1, offset >> 8); 34 | port::outb(vport, 15); 35 | port::outb(vport | 1, offset); 36 | } 37 | 38 | void push(void) { 39 | if (show_cursor) { 40 | move(X, Y); 41 | } 42 | } 43 | 44 | void pull(void) { 45 | int offset = 0; 46 | 47 | port::outb(vport, 14); 48 | offset = port::inb(vport | 1) << 8; 49 | port::outb(vport, 15); 50 | offset += port::inb(vport | 1); 51 | 52 | X = offset / VIDEO_MAX_COLUMN; 53 | Y = offset % VIDEO_MAX_COLUMN; 54 | } 55 | 56 | } /* cursor */ 57 | 58 | uint16_t *get_cell_ptr(int row, int col) { 59 | uint16_t *base = (uint16_t *) VIDEO_BASE; 60 | if (row < 0 || row >= VIDEO_MAX_ROW) { 61 | return NULL; 62 | } 63 | if (col < 0 || col > VIDEO_MAX_COLUMN) { 64 | return NULL; 65 | } 66 | return base + col + row * VIDEO_MAX_COLUMN; 67 | } 68 | 69 | void scroll(void) { 70 | uint16_t tofill = 0x20 | (attrib << 8); 71 | int offset = VIDEO_MAX_COLUMN * 2; 72 | 73 | uint16_t *ptr_first = (uint16_t *) VIDEO_BASE; 74 | uint16_t *ptr_second = (uint16_t *) (VIDEO_BASE + offset); 75 | 76 | for (int i = 0; i < VIDEO_SIZE - VIDEO_MAX_COLUMN; ++i) { 77 | *ptr_first++ = *ptr_second++; 78 | } 79 | for (int i = 0; i < VIDEO_MAX_COLUMN; ++i) { 80 | *ptr_first++ = tofill; 81 | } 82 | } 83 | 84 | } 85 | 86 | int mkcolor(int fore, int back) { 87 | return (back << 4) | fore; 88 | } 89 | 90 | void setcolor(int color, bool reset) { 91 | if (reset) { 92 | uint8_t *ptr = (uint8_t *) VIDEO_BASE; 93 | 94 | ptr += 1; 95 | for (int i = 0; i < VIDEO_SIZE; ++i) { 96 | *ptr = color; 97 | ptr += 2; 98 | } 99 | } 100 | attrib = color; 101 | } 102 | 103 | void initialize(bool show_cursor) { 104 | cursor::vport = *(uint16_t *) 0x463; 105 | cursor::show_cursor = show_cursor; 106 | cursor::pull(); 107 | if (!show_cursor) { 108 | /* disable cursor by hiding it. */ 109 | cursor::move(VIDEO_MAX_ROW, 0); 110 | } 111 | 112 | attrib = mkcolor(default_fore_color, default_back_color); 113 | setcolor(attrib, true); 114 | clear(); 115 | } 116 | 117 | void clear(void) { 118 | uint16_t tofill = 0x20 | (attrib << 8); 119 | uint16_t *ptr = (uint16_t *) VIDEO_BASE; 120 | 121 | for (int i = 0; i < VIDEO_SIZE; ++i) { 122 | *ptr++ = tofill; 123 | } 124 | 125 | cursor::X = cursor::Y = 0; 126 | cursor::push(); 127 | port::wait(); 128 | } 129 | 130 | void bkcopy(const uint16_t *src) { 131 | memcpy((void *) VIDEO_BASE, (void *) src, VIDEO_SIZE * 2); 132 | 133 | cursor::X = cursor::Y = 0; 134 | cursor::push(); 135 | port::wait(); 136 | } 137 | 138 | void putch(char ch) { 139 | uint16_t *ptr = get_cell_ptr(cursor::X, cursor::Y); 140 | 141 | if (ptr == NULL) { 142 | return; 143 | } 144 | 145 | if (ch == '\b') { // Backspace 146 | if (cursor::Y != 0) { 147 | cursor::Y -= 1; 148 | } 149 | } else if (ch == '\t') { // Tab 150 | cursor::Y = (cursor::Y + 4) & ~3; 151 | } else if (ch == '\r') { // Carriage return 152 | cursor::Y = 0; 153 | } else if (ch == '\n') { // New Line 154 | cursor::Y = 0; 155 | cursor::X += 1; 156 | } else { 157 | *ptr = ch | (attrib << 8); 158 | cursor::Y += 1; 159 | } 160 | if (cursor::Y >= VIDEO_MAX_COLUMN) { 161 | cursor::Y = 0; 162 | cursor::X += 1; 163 | } 164 | while (cursor::X >= VIDEO_MAX_ROW) { 165 | scroll(); 166 | cursor::X -= 1; 167 | } 168 | cursor::push(); 169 | } 170 | 171 | } /* console */ 172 | -------------------------------------------------------------------------------- /kernel/krnl/irq.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | asmlinkage { 10 | 11 | void irq_handler0(); 12 | void irq_handler1(); 13 | void irq_handler2(); 14 | void irq_handler3(); 15 | void irq_handler4(); 16 | void irq_handler5(); 17 | void irq_handler6(); 18 | void irq_handler7(); 19 | void irq_handler8(); 20 | void irq_handler9(); 21 | void irq_handler10(); 22 | void irq_handler11(); 23 | void irq_handler12(); 24 | void irq_handler13(); 25 | void irq_handler14(); 26 | void irq_handler15(); 27 | 28 | } 29 | 30 | namespace irq { 31 | 32 | namespace { 33 | 34 | #define IRQ_VECTOR_OFFSET 32 35 | #define MAX_IRQ_ENTRY 16 36 | 37 | #define PORT_MASTER_PIC_CMD 0x20 38 | #define PORT_MASTER_PIC_DAT 0x21 39 | #define PORT_SLAVE_PIC_CMD 0xa0 40 | #define PORT_SLAVE_PIC_DAT 0xa1 41 | 42 | fn_irq_handler_t lpfn_irq_handler[MAX_IRQ_ENTRY]; 43 | 44 | void dispatcher(irq_context_t *ptr) { 45 | int irq_index = ptr->irq_index; 46 | fn_irq_handler_t handler = lpfn_irq_handler[irq_index]; 47 | 48 | if (handler == NULL) { 49 | printf("[IRQ dispatcher] Unhandled IRQ %d.\n", irq_index); 50 | } else { 51 | handler(ptr); 52 | } 53 | 54 | /* send an EOI (end of interrupt) to indicate that we are done. */ 55 | if (irq_index >= 8) { 56 | port::outb(PORT_SLAVE_PIC_CMD, 0x20); 57 | } 58 | port::outb(PORT_MASTER_PIC_CMD, 0x20); 59 | } 60 | 61 | } 62 | 63 | void initialize(void) { 64 | /* remap IRQ to proper IDT entries (32 ~ 47) */ 65 | port::outb(PORT_MASTER_PIC_CMD, 0x11); 66 | port::outb(PORT_SLAVE_PIC_CMD, 0x11); 67 | port::outb(PORT_MASTER_PIC_DAT, IRQ_VECTOR_OFFSET); // vector offset for master PIC is 32 68 | port::outb(PORT_SLAVE_PIC_DAT, IRQ_VECTOR_OFFSET + 8); // vector offset for slave PIC is 40 69 | port::outb(PORT_MASTER_PIC_DAT, 0x4); // tell master PIC that there is a slave PIC at IRQ2 70 | port::outb(PORT_SLAVE_PIC_DAT, 0x2); // tell slave PIC its cascade identity 71 | port::outb(PORT_MASTER_PIC_DAT, 0x1); 72 | port::outb(PORT_SLAVE_PIC_DAT, 0x1); 73 | /* disable all IRQs by default. */ 74 | port::outb(PORT_MASTER_PIC_DAT, 0xff); 75 | port::outb(PORT_SLAVE_PIC_DAT, 0xff); 76 | 77 | /* initialize IRQ to correct entries in the IDT. */ 78 | #define set_irq(n) idt::set_gate(n + IRQ_VECTOR_OFFSET, (uint32_t) irq_handler##n, KERNEL_CODE_SEL, 0x8e); 79 | set_irq(0); 80 | set_irq(1); 81 | set_irq(2); 82 | set_irq(3); 83 | set_irq(4); 84 | set_irq(5); 85 | set_irq(6); 86 | set_irq(7); 87 | set_irq(8); 88 | set_irq(9); 89 | set_irq(10); 90 | set_irq(11); 91 | set_irq(12); 92 | set_irq(13); 93 | set_irq(14); 94 | set_irq(15); 95 | #undef set_irq 96 | 97 | memset(lpfn_irq_handler, 0, sizeof(lpfn_irq_handler)); 98 | } 99 | 100 | /* install an IRQ handler. */ 101 | void install(int index, fn_irq_handler_t handler) { 102 | if (lpfn_irq_handler[index] != NULL) { 103 | printf("[irq::install] IRQ%d handler already exists, overwritten.\n", index); 104 | } 105 | lpfn_irq_handler[index] = handler; 106 | } 107 | 108 | /* uninstall an IRQ handler. */ 109 | void uninstall(int index) { 110 | if (lpfn_irq_handler[index] == NULL) { 111 | printf("[irq::uninstall] IRQ%d handler not exists.\n", index); 112 | } 113 | lpfn_irq_handler[index] = NULL; 114 | } 115 | 116 | /* disable an IRQ by setting mask. */ 117 | void disable(int index) { 118 | uint16_t port; 119 | uint8_t value; 120 | 121 | if (index < 8) { 122 | port = PORT_MASTER_PIC_DAT; 123 | } else { 124 | port = PORT_SLAVE_PIC_DAT; 125 | index -= 8; 126 | } 127 | value = port::inb(port) | (1 << index); 128 | port::outb(port, value); 129 | } 130 | 131 | /* enable an IRQ by clearing mask. */ 132 | void enable(int index) { 133 | uint16_t port; 134 | uint8_t value; 135 | 136 | if (index < 8) { 137 | port = PORT_MASTER_PIC_DAT; 138 | } else { 139 | port = PORT_SLAVE_PIC_DAT; 140 | index -= 8; 141 | } 142 | value = port::inb(port) & ~(1 << index); 143 | port::outb(port, value); 144 | } 145 | 146 | } /* irq */ 147 | 148 | asmlinkage void irq_dispatcher(irq::irq_context_t *ptr) { 149 | irq::dispatcher(ptr); 150 | } 151 | -------------------------------------------------------------------------------- /kernel/util/xprintf.cc: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------/ 2 | / Universal string handler for user console interface 3 | /-------------------------------------------------------------------------/ 4 | / 5 | / Copyright (C) 2011, ChaN, all right reserved. 6 | / 7 | / * This software is a free software and there is NO WARRANTY. 8 | / * No restriction on use. You can use, modify and redistribute it for 9 | / personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. 10 | / * Redistributions of source code must retain the above copyright notice. 11 | / 12 | / ported to BadAppleOS by foreverbell at 2014/8/5. 13 | / 14 | /-------------------------------------------------------------------------*/ 15 | 16 | 17 | #include "xprintf.h" 18 | #include 19 | 20 | static char *outptr; 21 | 22 | /*----------------------------------------------*/ 23 | /* Put a character */ 24 | /*----------------------------------------------*/ 25 | 26 | void putc (char c) 27 | { 28 | 29 | if (outptr) { 30 | *outptr++ = (unsigned char)c; 31 | return; 32 | } 33 | 34 | console::putch((unsigned char)c); 35 | } 36 | 37 | 38 | /*----------------------------------------------*/ 39 | /* Put a null-terminated string */ 40 | /*----------------------------------------------*/ 41 | 42 | void puts ( /* Put a string to the default device */ 43 | const char* str /* Pointer to the string */ 44 | ) 45 | { 46 | while (*str) 47 | putc(*str++); 48 | putc('\n'); 49 | } 50 | 51 | 52 | /*----------------------------------------------*/ 53 | /* Formatted string output */ 54 | /*----------------------------------------------*/ 55 | /* printf("%d", 1234); "1234" 56 | printf("%6d,%3d%%", -200, 5); " -200, 5%" 57 | printf("%-6u", 100); "100 " 58 | printf("%ld", 12345678L); "12345678" 59 | printf("%04x", 0xA3); "00a3" 60 | printf("%08LX", 0x123ABC); "00123ABC" 61 | printf("%016b", 0x550F); "0101010100001111" 62 | printf("%s", "String"); "String" 63 | printf("%-4s", "abc"); "abc " 64 | printf("%4s", "abc"); " abc" 65 | printf("%c", 'a'); "a" 66 | printf("%f", 10.0); 67 | */ 68 | 69 | static 70 | void vprintf ( 71 | const char* fmt, /* Pointer to the format string */ 72 | va_list arp /* Pointer to arguments */ 73 | ) 74 | { 75 | unsigned int r, i, j, w, f; 76 | unsigned long v; 77 | char s[16], c, d, *p; 78 | 79 | 80 | for (;;) { 81 | c = *fmt++; /* Get a char */ 82 | if (!c) break; /* End of format? */ 83 | if (c != '%') { /* Pass through it if not a % sequense */ 84 | putc(c); continue; 85 | } 86 | f = 0; 87 | c = *fmt++; /* Get first char of the sequense */ 88 | if (c == '0') { /* Flag: '0' padded */ 89 | f = 1; c = *fmt++; 90 | } else { 91 | if (c == '-') { /* Flag: left justified */ 92 | f = 2; c = *fmt++; 93 | } 94 | } 95 | for (w = 0; c >= '0' && c <= '9'; c = *fmt++) /* Minimum width */ 96 | w = w * 10 + c - '0'; 97 | if (c == 'l' || c == 'L') { /* Prefix: Size is long int */ 98 | f |= 4; c = *fmt++; 99 | } 100 | if (!c) break; /* End of format? */ 101 | d = c; 102 | if (d >= 'a') d -= 0x20; 103 | switch (d) { /* Type is... */ 104 | case 'S' : /* String */ 105 | p = va_arg(arp, char*); 106 | for (j = 0; p[j]; j++) ; 107 | while (!(f & 2) && j++ < w) putc(' '); 108 | while (*p) putc(*p++); 109 | while (j++ < w) putc(' '); 110 | continue; 111 | case 'C' : /* Character */ 112 | putc((char)va_arg(arp, int)); continue; 113 | case 'B' : /* Binary */ 114 | r = 2; break; 115 | case 'O' : /* Octal */ 116 | r = 8; break; 117 | case 'D' : /* Signed decimal */ 118 | case 'U' : /* Unsigned decimal */ 119 | r = 10; break; 120 | case 'X' : /* Hexdecimal */ 121 | r = 16; break; 122 | default: /* Unknown type (passthrough) */ 123 | putc(c); continue; 124 | } 125 | 126 | /* Get an argument and put it in numeral */ 127 | v = (f & 4) ? va_arg(arp, long) : ((d == 'D') ? (long)va_arg(arp, int) : (long)va_arg(arp, unsigned int)); 128 | if (d == 'D' && (v & 0x80000000)) { 129 | v = 0 - v; 130 | f |= 8; 131 | } 132 | i = 0; 133 | do { 134 | d = (char)(v % r); v /= r; 135 | if (d > 9) d += (c == 'x') ? 0x27 : 0x07; 136 | s[i++] = d + '0'; 137 | } while (v && i < sizeof(s)); 138 | if (f & 8) s[i++] = '-'; 139 | j = i; d = (f & 1) ? '0' : ' '; 140 | while (!(f & 2) && j++ < w) putc(d); 141 | do putc(s[--i]); while(i); 142 | while (j++ < w) putc(' '); 143 | } 144 | } 145 | 146 | 147 | void printf ( /* Put a formatted string to the default device */ 148 | const char* fmt, /* Pointer to the format string */ 149 | ... /* Optional arguments */ 150 | ) 151 | { 152 | va_list arp; 153 | 154 | 155 | va_start(arp, fmt); 156 | vprintf(fmt, arp); 157 | va_end(arp); 158 | } 159 | 160 | 161 | void xsprintf ( /* Put a formatted string to the memory */ 162 | char* buff, /* Pointer to the output buffer */ 163 | const char* fmt, /* Pointer to the format string */ 164 | ... /* Optional arguments */ 165 | ) 166 | { 167 | va_list arp; 168 | 169 | 170 | outptr = buff; /* Switch destination for memory */ 171 | 172 | va_start(arp, fmt); 173 | vprintf(fmt, arp); 174 | va_end(arp); 175 | 176 | *outptr = 0; /* Terminate output string with a \0 */ 177 | outptr = 0; /* Switch destination for device */ 178 | } 179 | 180 | 181 | -------------------------------------------------------------------------------- /kernel/stl/vector: -------------------------------------------------------------------------------- 1 | #ifndef __VECTOR__ 2 | #define __VECTOR__ 3 | 4 | #include 5 | #include 6 | 7 | namespace std { 8 | 9 | template > 11 | class vector { 12 | public: 13 | typedef typename allocator_type::value_type 14 | value_type, *iterator, *pointer, &reference; 15 | typedef const typename allocator_type::value_type 16 | *const_iterator, *const_pointer, &const_reference; 17 | typedef ptrdiff_t difference_type; 18 | typedef typename allocator_type::size_type size_type; 19 | 20 | vector(size_type capacity = 8) 21 | { 22 | vector_size = 0; buff_size = capacity; 23 | buff = (pointer)alloc.allocate(buff_size * sizeof(type)); 24 | } 25 | 26 | vector(const vector &v) 27 | { 28 | vector_size = v.vector_size; buff_size = v.buff_size; 29 | buff = (pointer)alloc.allocate(buff_size * sizeof(type)); 30 | 31 | for (size_type pos = 0; pos != vector_size; pos++) 32 | alloc.construct(&buff[pos], v.buff[pos]); 33 | } 34 | 35 | ~vector() 36 | { 37 | clear(); 38 | alloc.deallocate(buff, buff_size); 39 | } 40 | 41 | void swap(vector &v) 42 | { 43 | std::swap(buff, v.buff); 44 | std::swap(vector_size, v.vector_size); 45 | std::swap(buff_size, v.buff_size); 46 | } 47 | 48 | void assign(const vector &v) 49 | { 50 | clear(); 51 | insert(begin(), v.begin(), v.end()); 52 | } 53 | 54 | void assign(size_type count, const_reference val) 55 | { 56 | clear(); 57 | insert(begin(), count, val); 58 | } 59 | 60 | void reserve(size_type need) 61 | { 62 | if (need <= buff_size) return; 63 | 64 | vector v(need); 65 | v.insert(v.begin(), begin(), end()); 66 | 67 | swap(v); 68 | } 69 | 70 | void insert(iterator where, size_type count, const_reference val) 71 | { 72 | if (count == 0) return; 73 | size_type offset = where - begin(); 74 | 75 | if (count < (vector_size >> 1)) 76 | reserve(vector_size + (vector_size >> 1)); 77 | else 78 | reserve(vector_size + count); 79 | 80 | where = begin() + offset; 81 | for (iterator iter = end(); iter != where; --iter) { 82 | alloc.construct(&*iter, *(iter - count)); 83 | alloc.destroy(iter - count); 84 | } 85 | 86 | for (size_type pos = 0; pos != count; pos++) 87 | alloc.construct(&*where + pos, val); 88 | 89 | vector_size += count; 90 | } 91 | 92 | void insert(iterator where, iterator first, iterator last) 93 | { 94 | size_type count = last - first; 95 | if (count == 0) return; 96 | size_type offset = where - begin(); 97 | 98 | if (count < (vector_size >> 1)) 99 | reserve(vector_size + (vector_size >> 1)); 100 | else 101 | reserve(vector_size + count); 102 | 103 | where = begin() + offset; 104 | for (iterator iter = end(); iter != where; ) { 105 | --iter; 106 | alloc.construct(&*(iter + count), *iter); 107 | alloc.destroy(iter); 108 | } 109 | 110 | for (; first != last; where++, first++) 111 | alloc.construct(&*where, *first); 112 | 113 | vector_size += count; 114 | } 115 | 116 | void insert(iterator where, const_reference val) 117 | { insert(where, 1, val); } 118 | 119 | void erase(iterator first, iterator last) 120 | { 121 | size_type count = last - first; 122 | if (count == 0) return; 123 | 124 | for (; last != end(); first++, last++) { 125 | alloc.destroy(&*first); 126 | alloc.construct(&*first, *last); 127 | } 128 | 129 | for (; first != end(); first++) 130 | alloc.destroy(first); 131 | 132 | vector_size -= count; 133 | } 134 | 135 | void erase(iterator where) { erase(where, where + 1); } 136 | void clear() { erase(begin(), end()); } 137 | 138 | void resize(size_type newsize, const_reference val) 139 | { 140 | if (newsize > vector_size) 141 | insert(end(), newsize - vector_size, val); 142 | else 143 | erase(end() - (vector_size - newsize), end()); 144 | } 145 | 146 | void resize(size_type newsize) { resize(newsize, type()); } 147 | 148 | pointer data() { return buff; } 149 | const_pointer data() const { return buff; } 150 | 151 | reference front() { return buff[0]; } 152 | reference back() { return buff[vector_size - 1]; } 153 | iterator begin() { return buff; } 154 | iterator end() { return buff + vector_size; } 155 | 156 | const_reference front() const { return buff[0]; } 157 | const_reference back() const { return buff[vector_size - 1]; } 158 | const_iterator begin() const { return buff; } 159 | const_iterator end() const { return buff + vector_size; } 160 | 161 | void push_back(const_reference val) { insert(end(), val); } 162 | void pop_back() { erase(end() - 1); } 163 | 164 | size_type size() const { return vector_size; } 165 | size_type capacity() const { return buff_size; } 166 | allocator_type get_allocator() const { return alloc; } 167 | size_type max_size() const { return alloc.max_size(); } 168 | 169 | vector & operator =(const vector v) 170 | { 171 | assign(v); 172 | return *this; 173 | } 174 | 175 | reference operator [] (size_type pos) 176 | { return *(begin() + pos); } 177 | 178 | const_reference operator [] (size_type pos) const 179 | { return *(begin() + pos); } 180 | 181 | reference at(size_type pos) 182 | { 183 | if (pos >= vector_size) 184 | std::bad_alloc("[vector] invalid vector subscript"); 185 | 186 | return *(begin() + pos); 187 | } 188 | 189 | const_reference at(size_type pos) const 190 | { 191 | if (pos >= vector_size) 192 | std::bad_alloc("[vector] invalid vector subscript"); 193 | 194 | return *(begin() + pos); 195 | } 196 | 197 | private: 198 | pointer buff; 199 | size_type vector_size, buff_size; 200 | allocator_type alloc; 201 | }; 202 | 203 | } /* std */ 204 | 205 | #endif 206 | -------------------------------------------------------------------------------- /kernel/ba/video.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "badapple.h" 10 | 11 | using std::vector; 12 | using std::pair; 13 | using std::make_pair; 14 | 15 | namespace badapple { 16 | 17 | extern uint8_t vdatas[] asm("_binary_vdata_bin_start"); 18 | extern uint8_t vdatae[] asm("_binary_vdata_bin_end"); 19 | 20 | int video::progress() const { 21 | return (cur_frame + 1) * 100 / count; 22 | } 23 | 24 | bool video::has_next() const { 25 | return cur_frame < count; 26 | } 27 | 28 | void video::next() { 29 | console::bkcopy(pool + cur_frame * VIDEO_SIZE); 30 | cur_frame += 1; 31 | } 32 | 33 | void video::free(void) { 34 | delete [] pool; 35 | } 36 | 37 | video::video() { 38 | #ifdef VIDEO_SQUARE_TEXT 39 | const uint16_t bkcolor1 = console::mkcolor(console::vga_color::white, console::vga_color::black); 40 | const uint16_t bkcolor2 = console::mkcolor(console::vga_color::black, console::vga_color::white); 41 | #else 42 | const uint16_t acolor = console::mkcolor(console::vga_color::light_grey, console::vga_color::black); 43 | #endif 44 | 45 | srand(1024); 46 | 47 | decompressor decomp(vdatas, vdatae); 48 | 49 | cur_frame = 0; 50 | count = decomp.frame_count(); 51 | pool = new uint16_t[count * VIDEO_SIZE]; 52 | 53 | printf("[video] Frame count = %d.\n", count); 54 | printf("[video] Pool address = 0x%x.\n", (int) pool); 55 | 56 | stream reader = stream(decomp.data(), decomp.data_end()); 57 | uint16_t *pool_ptr = pool; 58 | uint16_t attrib; 59 | 60 | while (reader.has_next()) { 61 | int bit = reader.nextb(); 62 | #ifdef VIDEO_SQUARE_TEXT 63 | attrib = (bit ? bkcolor1 : bkcolor2) << 8; 64 | #else 65 | attrib = (acolor << 8) | (bit ? ' ' : '#'); 66 | #endif 67 | *pool_ptr++ = attrib; 68 | } 69 | printf("[video] Data loaded, tailing pointer = 0x%x.\n", (int) pool_ptr); 70 | 71 | #ifndef VIDEO_SQUARE_TEXT 72 | puts("[video] Artifying data, this may take a few second."); 73 | artify(); 74 | puts("[video] Data artified."); 75 | #endif 76 | } 77 | 78 | std::pair video::find(int x) { 79 | if (dsu[x].first != x) { 80 | dsu[x] = find(dsu[x].first); 81 | } 82 | return dsu[x]; 83 | } 84 | 85 | void video::join(int x, int y) { 86 | x = find(x).first; 87 | y = find(y).first; 88 | if (x != y) { 89 | if (dsu[x].second.ch != '?') { 90 | std::swap(x, y); 91 | } 92 | dsu[x].first = dsu[y].first; 93 | dsu[y].second.size += dsu[x].second.size; 94 | } 95 | } 96 | 97 | void video::artify() { 98 | const int dxy[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; 99 | 100 | const char dot_chars[] = {'\'', '`', ',', '.'}; 101 | const char line_chars[] = {'\\', '/', char(28), '^'}; 102 | const char dense_chars[] = {'?', 'o', '%'}; 103 | const int dense_count = sizeof(dense_chars) / sizeof(char); 104 | 105 | dsu.resize(VIDEO_SIZE); 106 | 107 | auto at = [&](int frame, int x, int y) -> char { 108 | const uint16_t *p = pool; 109 | p += frame * VIDEO_SIZE; 110 | p += y * VIDEO_MAX_COLUMN; 111 | p += x; 112 | return *(const char *) p; 113 | }; 114 | auto set = [&](int frame, int x, int y, char ch) -> void { 115 | const uint16_t *p = pool; 116 | p += frame * VIDEO_SIZE; 117 | p += y * VIDEO_MAX_COLUMN; 118 | p += x; 119 | *(char *) p = ch; 120 | }; 121 | auto label = [&](int x, int y) -> int { 122 | return y * VIDEO_MAX_COLUMN + x; 123 | }; 124 | auto within = [&](int x, int y) -> bool { 125 | return x >= 0 && x < VIDEO_MAX_COLUMN && y >= 0 && y < VIDEO_MAX_ROW; 126 | }; 127 | 128 | /* randomly change connected block to one dense char. */ 129 | for (int f = 0; f < count; ++f) { 130 | for (int x = 0; x < VIDEO_MAX_COLUMN; ++x) { 131 | for (int y = 0; y < VIDEO_MAX_ROW; ++y) { 132 | char ch = at(f, x, y); 133 | if (ch != ' ') { 134 | if (f > 0 && at(f - 1, x, y) != ' ') { 135 | ch = at(f - 1, x, y); 136 | } else { 137 | ch = '?'; // to be determinated. 138 | } 139 | } 140 | dsu[label(x, y)] = make_pair(label(x, y), (info_t) {ch, 1} ); 141 | } 142 | } 143 | for (int x = 0; x < VIDEO_MAX_COLUMN; ++x) { 144 | for (int y = 0; y < VIDEO_MAX_ROW; ++y) { 145 | if (at(f, x, y) == ' ') { 146 | continue; 147 | } 148 | for (int d = 0; d < 4; ++d) { 149 | if (!within(x + dxy[d][0], y + dxy[d][1]) || at(f, x + dxy[d][0], y + dxy[d][1]) == ' ') { 150 | continue; 151 | } 152 | join(label(x, y), label(x + dxy[d][0], y + dxy[d][1])); 153 | } 154 | } 155 | } 156 | for (int x = 0; x < VIDEO_MAX_COLUMN; ++x) { 157 | for (int y = 0; y < VIDEO_MAX_ROW; ++y) { 158 | int father = find(label(x, y)).first; 159 | char &ch = dsu[father].second.ch; 160 | /* MSB=1 <---> mutated. */ 161 | if (ch == '?') { 162 | ch = dense_chars[rand() % dense_count] | 0x80; 163 | } else if (ch > 0 && ch != ' ' && dsu[father].second.size < 100) { 164 | /* 10% possibility to get a mutation. */ 165 | ch = rand() % 10 == 0 ? dense_chars[rand() % dense_count] : ch; 166 | ch |= 0x80; 167 | } 168 | set(f, x, y, ch & 0x7f); 169 | } 170 | } 171 | } 172 | 173 | /* emphasize the boundary. */ 174 | for (int f = 0; f < count; ++f) { 175 | for (int x = 0; x < VIDEO_MAX_COLUMN; ++x) { 176 | for (int y = 0; y < VIDEO_MAX_ROW; ++y) { 177 | if (at(f, x, y) == ' ') { 178 | continue; 179 | } 180 | int dir = -1, empty = 0; 181 | for (int d = 0; d < 4; ++d) { 182 | if (!within(x + dxy[d][0], y + dxy[d][1])) { 183 | continue; 184 | } 185 | if (at(f, x + dxy[d][0], y + dxy[d][1]) == ' ') { 186 | dir = d; 187 | empty += 1; 188 | } 189 | } 190 | if (dir != -1) { 191 | bool use_line = true; 192 | if (empty > 2) { 193 | use_line = false; 194 | } 195 | if (within(x + dxy[(3 - dir) ^ 1][0], y + dxy[(3 - dir) ^ 1][1]) && 196 | at(f, x + dxy[(3 - dir) ^ 1][0], y + dxy[(3 - dir) ^ 1][1]) == ' ') { 197 | use_line = false; 198 | } 199 | if (within(x + dxy[3 - dir][0], y + dxy[3 - dir][1]) && 200 | at(f, x + dxy[3 - dir][0], y + dxy[3 - dir][1]) == ' ') { 201 | use_line = false; 202 | } 203 | set(f, x, y, use_line ? line_chars[dir] : dot_chars[dir]); 204 | } 205 | } 206 | } 207 | } 208 | } 209 | 210 | } /* badapple */ 211 | --------------------------------------------------------------------------------