diff --git a/Makefile b/Makefile index 65486dc..98e148d 100644 --- a/Makefile +++ b/Makefile @@ -166,7 +166,9 @@ clean: bootdisk: $(OBJDIR)/bootdisk.iso @echo "CP $<" $(VERBOSE) cp $< $(DRIVE_HD) - +#bootdisk: $(OBJDIR)/bootdisk.iso +# sudo dd if=$(OBJDIR)/bootdisk.iso of=$(DRIVE_HD) bs=4M conv=fsync status=progress +# sync # -------------------------------------------------------------------------- # 'qemu' runs the QEMU emulator with the system diff --git a/device/cgastr.cc b/device/cgastr.cc index 2af3270..f52830b 100644 --- a/device/cgastr.cc +++ b/device/cgastr.cc @@ -5,7 +5,7 @@ /* C G A _ S T R E A M */ /* */ /*---------------------------------------------------------------------------*/ -/* The CGA_Stream class allows to print different data types as text strings */ +/* The CGA_Stream class allows to print different data types as text strings / /* to a PC's CGA screen. */ /* For attributes/colors and cursor positioning use the methods of class */ /* CGA_Screen. */ @@ -14,6 +14,8 @@ #include "device/cgastr.h" +CGA_Stream kout; + void CGA_Stream::flush() { screen.print(buffer, pos, 0x07); diff --git a/device/cgastr.h b/device/cgastr.h index b88e7fb..8cd8f9f 100644 --- a/device/cgastr.h +++ b/device/cgastr.h @@ -29,6 +29,10 @@ class CGA_Stream : public O_Stream CGA_Stream& operator=(const CGA_Stream&) = delete; void flush() override; + void setpos(int x, int y) { screen.setpos(x, y); } + void getpos(int &x, int &y) { screen.getpos(x, y); } }; +extern CGA_Stream kout; + #endif diff --git a/device/keyboard.cc b/device/keyboard.cc new file mode 100644 index 0000000..c4c4df2 --- /dev/null +++ b/device/keyboard.cc @@ -0,0 +1,45 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* K E Y B O A R D */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Keyboard driver. */ +/*****************************************************************************/ + +#include "device/keyboard.h" +#include "machine/plugbox.h" +#include "machine/pic.h" +#include "device/cgastr.h" + +Keyboard keyboard; + +void Keyboard::plugin() { + plugbox.assign(Plugbox::keyboard, *this); + pic.allow(PIC::keyboard); +} + +bool Keyboard::prologue() { + //kout<< "IRQ"<queued(false); + cpu.enable_int(); + item->epilogue(); + cpu.disable_int(); + leave(); + cpu.enable_int(); + }else{ + if(!item->queued()){ + item->queued(true); + queue.enqueue(item); + } + cpu.enable_int(); + } +} + +void Guard::leave(){ + cpu.disable_int(); + Gate *gate; + while((gate = (Gate*)queue.dequeue())){ + gate->queued(false); + cpu.enable_int(); + gate->epilogue(); + cpu.disable_int(); + } + retne(); + cpu.enable_int(); +} + +Guard guard; diff --git a/guard/guard.h b/guard/guard.h new file mode 100644 index 0000000..c74c6ad --- /dev/null +++ b/guard/guard.h @@ -0,0 +1,34 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* G U A R D */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Using this class, we can synchronize activities with a critical section */ +/* with interrupt handlers that also access this critical section. This */ +/* synchronization takes place along the prologue/epilogue model. */ +/*****************************************************************************/ + +#ifndef __Guard_include__ +#define __Guard_include__ + +/* Add your code here */ +#include "object/queue.h" +#include "locker.h" +#include "gate.h" +class Guard : public Locker { +private: + Queue queue; +public: + Guard (const Guard ©) = delete; // prevent copying + Guard& operator=(const Guard&) = delete; // prevent assignment + Guard () {} + + void leave(); + void relay(Gate *item); +}; + +extern Guard guard; + +#endif diff --git a/guard/guardian.cc b/guard/guardian.cc index d12d131..03bc944 100644 --- a/guard/guardian.cc +++ b/guard/guardian.cc @@ -10,15 +10,34 @@ /*****************************************************************************/ /* INCLUDES */ - +#include "machine/plugbox.h" +#include "machine/cpu.h" +#include "guard.h" +#include "device/cgastr.h" +#include "machine/io_port.h" /* FUNCTIONS */ extern "C" void guardian (unsigned int slot); /* GUARDIAN: Low-level interrupt handling. We will extend this function at */ /* a later point in time. */ - +extern Plugbox plugbox; // singleton +extern CPU cpu; void guardian (unsigned int slot) { - + // Spurious IRQ from master (slot 39) or slave (slot 47): + // In auto-EOI mode no manual EOI needed, just return. + if(slot == 39 || slot == 47){ + return; + } + + // Re-enable interrupts to allow higher-priority nesting + cpu.enable_int(); + + Gate &gate = plugbox.report(slot); + + if(gate.prologue()){ + // relay() handles the queued-check atomically under cli + guard.relay(&gate); + } } diff --git a/guard/locker.h b/guard/locker.h new file mode 100644 index 0000000..2075cf1 --- /dev/null +++ b/guard/locker.h @@ -0,0 +1,51 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* L O C K E R */ +/* */ +/*---------------------------------------------------------------------------*/ +/* The Locker class implements a lock that can be used to protect critical */ +/* sections. However, the variable only indicates whether the critical */ +/* section is free. Potentially necessary waiting, and protection for */ +/* counting functionality, must be implemented elsewhere. */ +/*****************************************************************************/ + +#ifndef __Locker_include__ +#define __Locker_include__ +#include "device/cgastr.h" +#include "machine/cpu.h" + +extern CPU cpu; + +class Locker { +private: + volatile bool locked; +public: + Locker() : locked(false) {} + Locker(const Locker ©) = delete; // prevent copying + Locker& operator=(const Locker&) = delete; // prevent assignment + + void enter(){ + if(locked){ + kout << "LOCK ERROR: double enter()" << endl; + cpu.halt(); + } + locked = true; + } + + void retne(){ + if(!locked){ + kout << "LOCK ERROR: double retne()" << endl; + cpu.halt(); + } + locked = false; + } + + bool avail(){ + return !locked; + } +/* Add your code here */ +}; + +#endif diff --git a/guard/secure.h b/guard/secure.h new file mode 100644 index 0000000..d737a27 --- /dev/null +++ b/guard/secure.h @@ -0,0 +1,32 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* S E C U R E */ +/* */ +/*---------------------------------------------------------------------------*/ +/* The Secure class eases locking and unlocking Locker variables that are */ +/* used to protect critical sections of the OS kernel. In the constructor, */ +/* this class takes the lock; in the destructor it releases it again. Thus, */ +/* it suffices to define a Secure object to protect its complete scope as a */ +/* critical section. */ +/*****************************************************************************/ + +#ifndef __Secure_include__ +#define __Secure_include__ +#include "guard.h" + +extern Guard guard; + +class Secure{ +public: + Secure(){ + guard.enter(); + } + + ~Secure(){ + guard.leave(); + } +}; + +#endif diff --git a/isofiles/boot/system b/isofiles/boot/system index 6526315..cb1c7a4 100755 Binary files a/isofiles/boot/system and b/isofiles/boot/system differ diff --git a/machine/cpu.asm b/machine/cpu.asm new file mode 100644 index 0000000..01bf503 --- /dev/null +++ b/machine/cpu.asm @@ -0,0 +1,53 @@ +;***************************************************************************** +;* Operating-System Construction * +;*---------------------------------------------------------------------------* +;* * +;* C P U * +;* * +;*---------------------------------------------------------------------------* +;* This assembler module implements functionality from class CPU. * +;***************************************************************************** + +; EXPORTED FUNCTIONS + +[GLOBAL int_enable] +[GLOBAL int_disable] +[GLOBAL cpu_idle] +[GLOBAL cpu_halt] + +; IMPLEMENTATION + +[SECTION .text] + +; INT_ENABLE: Allow (hardware) interrupts +; +; C prototype: void int_enable (); + +int_enable: + sti + ret + +; INT_DISABLE: Disallow/ignore interrupts +; +; C prototype: void int_disable (); + +int_disable: + cli + ret + +; CPU_IDLE : Enable interrupts and halt CPU until the next interrupt occurs +; +; C prototype: void cpu_idle (void) + +cpu_idle: + sti ; The STI / HLT sequence is executed atomically! + hlt + ret + +; CPU_HALT : Halt CPU (forever) +; +; C prototype: void cpu_halt (void) + +cpu_halt: + cli + hlt diff --git a/machine/cpu.cc b/machine/cpu.cc new file mode 100644 index 0000000..c81019e --- /dev/null +++ b/machine/cpu.cc @@ -0,0 +1,3 @@ +#include "machine/cpu.h" + +CPU cpu; diff --git a/machine/cpu.h b/machine/cpu.h new file mode 100644 index 0000000..f4423a5 --- /dev/null +++ b/machine/cpu.h @@ -0,0 +1,39 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* C P U */ +/* */ +/*---------------------------------------------------------------------------*/ +/* An abstraction for the CPU. */ +/* Currently allows enabling/disabling interrupts and halting the CPU. */ +/*****************************************************************************/ + +#ifndef __CPU_include__ +#define __CPU_include__ + +// These functions are implemented in cpu.asm. +extern "C" void int_enable (); +extern "C" void int_disable (); +extern "C" void cpu_idle (); +extern "C" void cpu_halt (); + +class CPU { +public: + CPU() {} + CPU(const CPU ©) = delete; // prevent copying + CPU& operator=(const CPU&) = delete; // prevent assignment + // Allow (hardware) interrupts + inline void enable_int() { int_enable(); } + + // Disallow/ignore interrupts + inline void disable_int() { int_disable(); } + + // Enable interrupts and halt CPU until the next interrupt occurs + inline void idle() { cpu_idle(); } + + // Halt CPU (forever) + inline void halt() { cpu_halt(); } +}; + +#endif diff --git a/machine/keyctrl.cc b/machine/keyctrl.cc index 148d24e..e9434aa 100644 --- a/machine/keyctrl.cc +++ b/machine/keyctrl.cc @@ -11,6 +11,7 @@ /* INCLUDES */ #include "machine/keyctrl.h" +#include "machine/pic.h" /* STATIC MEMBERS */ @@ -230,22 +231,22 @@ Keyboard_Controller::Keyboard_Controller() : leds(0), ctrl_port(0x64), data_port Key Keyboard_Controller::key_hit() { - Key invalid; - - int status = ctrl_port.inb(); - - if (!(status & outb)) - return invalid; - - if (status & auxb) - return invalid; + Key invalid; + int status; - code = data_port.inb(); + // Defensive check: process all available bytes in the buffer + while ((status = ctrl_port.inb()) & outb) { + if (status & auxb) { + data_port.inb(); // ignore mouse data + continue; + } - if (key_decoded()) - return gather; + code = data_port.inb(); + if (key_decoded()) + return gather; + } - return invalid; + return invalid; } // REBOOT: Reboots the PC. Yes, in a PC the keyboard controller is @@ -279,21 +280,40 @@ void Keyboard_Controller::reboot() void Keyboard_Controller::set_repeat_rate(int speed, int delay) { + bool masked = pic.is_masked(PIC::keyboard); + pic.forbid(PIC::keyboard); + // Wait until input buffer is empty while (ctrl_port.inb() & inpb) ; data_port.outb(kbd_cmd::set_speed); // 0xF3 + // Wait for ACK + while (!(ctrl_port.inb() & outb)) + ; + data_port.inb(); // consume ACK + // Wait until input buffer is empty again while (ctrl_port.inb() & inpb) ; data_port.outb(((delay & 0x3) << 5) | (speed & 0x1f)); + + // Wait for ACK + while (!(ctrl_port.inb() & outb)) + ; + data_port.inb(); // consume ACK + + if (!masked) + pic.allow(PIC::keyboard); } // SET_LED: sets or clears the specified LED void Keyboard_Controller::set_led(char led, bool on) { + bool masked = pic.is_masked(PIC::keyboard); + pic.forbid(PIC::keyboard); + if (on) leds |= led; // set bit else @@ -304,8 +324,21 @@ void Keyboard_Controller::set_led(char led, bool on) ; data_port.outb(kbd_cmd::set_led); // 0xED + // Wait for ACK + while (!(ctrl_port.inb() & outb)) + ; + data_port.inb(); // consume ACK + // Wait until input buffer is empty again while (ctrl_port.inb() & inpb) ; data_port.outb(leds); + + // Wait for ACK + while (!(ctrl_port.inb() & outb)) + ; + data_port.inb(); // consume ACK + + if (!masked) + pic.allow(PIC::keyboard); } diff --git a/machine/pic.cc b/machine/pic.cc new file mode 100644 index 0000000..e6fa460 --- /dev/null +++ b/machine/pic.cc @@ -0,0 +1,38 @@ +#include "machine/pic.h" + +PIC pic; + +PIC::PIC() : master_mask(0x21), slave_mask(0xa1) { +} + +void PIC::allow(int interrupt_device) { + if (interrupt_device < 8) { + unsigned char mask = master_mask.inb(); + mask &= ~(1 << interrupt_device); + master_mask.outb(mask); + } else { + unsigned char mask = slave_mask.inb(); + mask &= ~(1 << (interrupt_device - 8)); + slave_mask.outb(mask); + } +} + +void PIC::forbid(int interrupt_device) { + if (interrupt_device < 8) { + unsigned char mask = master_mask.inb(); + mask |= (1 << interrupt_device); + master_mask.outb(mask); + } else { + unsigned char mask = slave_mask.inb(); + mask |= (1 << (interrupt_device - 8)); + slave_mask.outb(mask); + } +} + +bool PIC::is_masked(int interrupt_device) { + if (interrupt_device < 8) { + return (master_mask.inb() & (1 << interrupt_device)) != 0; + } else { + return (slave_mask.inb() & (1 << (interrupt_device - 8))) != 0; + } +} diff --git a/machine/pic.h b/machine/pic.h new file mode 100644 index 0000000..41395fa --- /dev/null +++ b/machine/pic.h @@ -0,0 +1,25 @@ +#ifndef __pic_include__ +#define __pic_include__ + +#include "machine/io_port.h" + +class PIC { +public: + enum { timer = 0, keyboard = 1 }; + + PIC(const PIC ©) = delete; // prevent copying + PIC& operator=(const PIC&) = delete; // prevent assignment + PIC(); + + void allow (int interrupt_device); + void forbid (int interrupt_device); + bool is_masked (int interrupt_device); + +private: + IO_Port master_mask; + IO_Port slave_mask; +}; + +extern PIC pic; + +#endif diff --git a/machine/pit.cc b/machine/pit.cc new file mode 100644 index 0000000..c22cc1f --- /dev/null +++ b/machine/pit.cc @@ -0,0 +1,24 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* P I T */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Programmable Interval Timer. */ +/*****************************************************************************/ + +/* Add your code here */ +#include "pit.h" +#include "io_port.h" +void PIT::interval(int us){ + us_interval = us; + unsigned short divisor = (1193182UL * us)/1000000;// 1,193,182 Hz is PIT input clk freq + // int every 1 ms + IO_Port command(0x43);// PIT command reg + IO_Port data(0x40);// data reg for channel 0 aka system timer of PIT + + command.outb(0x36); // channel 0 LSB/MSB mode 3 + data.outb(divisor & 0xff); // LSB + data.outb(divisor >> 8); // MSB +} diff --git a/machine/pit.h b/machine/pit.h new file mode 100644 index 0000000..8298a3f --- /dev/null +++ b/machine/pit.h @@ -0,0 +1,31 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* P I T */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Programmable Interval Timer. */ +/*****************************************************************************/ + +#ifndef __pit_include__ +#define __pit_include__ + +class PIT { +public: + PIT(const PIT ©) = delete; // prevent copying + PIT& operator=(const PIT&) = delete; // prevent assignment +/* Add your code here */ + int us_interval; +public: + PIT(int us) { + interval (us); + } + int interval() { +/* Add your code here */ + return us_interval; + } + void interval(int us); +}; + +#endif diff --git a/machine/plugbox.cc b/machine/plugbox.cc new file mode 100644 index 0000000..a2278a0 --- /dev/null +++ b/machine/plugbox.cc @@ -0,0 +1,32 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* P L U G B O X */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Abstracts an interrupt vector table. Allows to configure handler routines */ +/* for every hardware and software interrupt and every CPU exception. */ +/*****************************************************************************/ + +/* Add your code here */ +#include "machine/plugbox.h" +#include "device/panic.h" + +Plugbox plugbox; + +Plugbox::Plugbox(){ + for(int i=0;i<64;i++){ + slots[i] = &panic; + } +} + +void Plugbox::assign(unsigned int slot, Gate& gate){ + if(slot<64){ + slots[slot] = &gate; + } +} + +Gate& Plugbox::report(unsigned int slot){ + return *slots[slot]; +} diff --git a/machine/plugbox.h b/machine/plugbox.h new file mode 100644 index 0000000..b5dda2d --- /dev/null +++ b/machine/plugbox.h @@ -0,0 +1,33 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* P L U G B O X */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Abstracts an interrupt vector table. Allows to configure handler routines */ +/* for every hardware and software interrupt and every CPU exception. */ +/*****************************************************************************/ + +#ifndef __Plugbox_include__ +#define __Plugbox_include__ + +#include "guard/gate.h" + +class Plugbox { +public: + Plugbox(const Plugbox ©) = delete; // prevent copying + Plugbox& operator=(const Plugbox&) = delete; // prevent assignment + Plugbox(); + static const unsigned int timer = 32; + static const unsigned int keyboard = 33; + void assign(unsigned int slot, Gate& gate); + Gate& report(unsigned int slot); +private: +/* Add your code here */ + Gate *slots[64]; +}; + +extern Plugbox plugbox; + +#endif diff --git a/machine/toc.asm b/machine/toc.asm new file mode 100644 index 0000000..ce552a0 --- /dev/null +++ b/machine/toc.asm @@ -0,0 +1,58 @@ +;****************************************************************************** +;* Operating-System Construction * +;*----------------------------------------------------------------------------* +;* * +;* T O C . A S M * +;* * +;*----------------------------------------------------------------------------* +;* +;****************************************************************************** + +%include "machine/toc.inc" + +; EXPORTED FUNCTIONS + +[GLOBAL toc_switch] +[GLOBAL toc_go] + +; FUNCTION IMPLEMENTATIONS + +[SECTION .text] + +; TOC_GO: Starts the very first process. +; +; C prototype: void toc_go(struct toc* regs); + +toc_go: + mov rbx, [rdi + rbx_offset] + mov r12, [rdi + r12_offset] + mov r13, [rdi + r13_offset] + mov r14, [rdi + r14_offset] + mov r15, [rdi + r15_offset] + mov rbp, [rdi + rbp_offset] + mov rsp, [rdi + rsp_offset] + ret + +; TOC_SWITCH: Context switch. Saves the current register values and replaces +; them with values of the new "thread of control". +; +; C prototype: void toc_switch (struct toc* regs_now, +; struct toc* reg_then); + +toc_switch: + mov [rdi + rbx_offset], rbx + mov [rdi + r12_offset], r12 + mov [rdi + r13_offset], r13 + mov [rdi + r14_offset], r14 + mov [rdi + r15_offset], r15 + mov [rdi + rbp_offset], rbp + mov [rdi + rsp_offset], rsp + + mov rbx, [rsi + rbx_offset] + mov r12, [rsi + r12_offset] + mov r13, [rsi + r13_offset] + mov r14, [rsi + r14_offset] + mov r15, [rsi + r15_offset] + mov rbp, [rsi + rbp_offset] + mov rsp, [rsi + rsp_offset] + ret diff --git a/machine/toc.c b/machine/toc.c new file mode 100644 index 0000000..8f2d1da --- /dev/null +++ b/machine/toc.c @@ -0,0 +1,28 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* T O C */ +/* */ +/*---------------------------------------------------------------------------*/ +/* The toc struct is used to save the values of the non-volatile registers */ +/* in case of a context switch. toc_settle prepares the initial stack and */ +/* the toc struct for the first activation. */ +/*****************************************************************************/ + +#include "machine/toc.h" + +// TOC_SETTLE: Prepares a coroutine context for its first activation. +void toc_settle(struct toc *regs, void *tos, + void (*kickoff)(void *, void *, void *, void *, void *, void *, + void *), + void *object) +{ + void **stack = (void **)((unsigned long)tos & ~15UL); + + stack[-1] = object; + stack[-2] = 0; + stack[-3] = (void *)kickoff; + + regs->rsp = &stack[-3]; +} diff --git a/machine/toc.h b/machine/toc.h new file mode 100644 index 0000000..321a5b5 --- /dev/null +++ b/machine/toc.h @@ -0,0 +1,39 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* T O C */ +/* */ +/*---------------------------------------------------------------------------*/ +/* The toc struct is used to save the values of the non-volatile registers */ +/* in case of a context switch. */ +/* With the System V AMD64 ABI the GNU C compiler uses, RBP, RBX and R12 to */ +/* R15 are non-volatile registers that must be saved by hand at a context */ +/* switch. Additionally there are 108 bytes of non-volatile extended CPU */ +/* state that are only important if we use components like the FPU or SIMD */ +/* instructions. */ +/* */ +/* Careful: For accessing struct toc elements from an assembler function, we */ +/* define names for the necessary offsets from the struct's start in the */ +/* file toc.inc. To access the correct elements, definitions in toc.h and */ +/* toc.inc must match exactly. If you modify toc.h, you must also modify */ +/* toc.inc (and vice versa.) */ +/*****************************************************************************/ + +#ifndef __toc_include__ +#define __toc_include__ + +// TOC: ("thread of control") +// +struct toc { + void *rbx; + void *r12; + void *r13; + void *r14; + void *r15; + void *rbp; + void *rsp; + char fpu[108]; // optional: 108 bytes extended CPU state (MMX, SSE, ...) +}; + +#endif diff --git a/machine/toc.inc b/machine/toc.inc new file mode 100644 index 0000000..2081fb7 --- /dev/null +++ b/machine/toc.inc @@ -0,0 +1,29 @@ +;***************************************************************************** +;* Operating-System Construction * +;*---------------------------------------------------------------------------* +;* * +;* T O C * +;* * +;*---------------------------------------------------------------------------* +;* Assembler variant of struct toc definition from toc.h * +;* * +;* The register-name order must match that of struct toc (toc.h). * +;***************************************************************************** + +; STRUCTURES + +; TOC: non-volatile registers of an x86-64 CPU (System V AMD64 ABI) + +; The following definitions set rbx_offset to 0, r12_offset to 8, r13_offset +; to 16, and so on. This matches the offsets of elements rbx, r12, r13 etc. +; in struct toc (in bytes). + +[ABSOLUTE 0] +rbx_offset: resq 1 +r12_offset: resq 1 +r13_offset: resq 1 +r14_offset: resq 1 +r15_offset: resq 1 +rbp_offset: resq 1 +rsp_offset: resq 1 +fpu_offset: resb 108 diff --git a/main.cc b/main.cc index 7c003bb..e486879 100644 --- a/main.cc +++ b/main.cc @@ -1,79 +1,38 @@ #include "device/cgastr.h" -#include "machine/cgascr.h" -#include "machine/keyctrl.h" -#include "object/o_stream.h" +#include "machine/cpu.h" +#include "machine/pic.h" +#include "machine/plugbox.h" +#include "device/keyboard.h" +#include "user/appl.h" +#include "user/loop.h" +#include "thread/scheduler.h" -int main() { - CGA_Screen screen; - Keyboard_Controller keyboard; - - screen.print(" ", 20, 0x07); +extern CPU cpu; +extern PIC pic; +extern Plugbox plugbox; +extern Keyboard keyboard; +extern CGA_Stream kout; - CGA_Stream kout; // stream formatter +Scheduler scheduler; - kout << "PurrOS 2.0.0" << endl; - kout << "Hello O_Stream!" << endl; - kout << "Number: " << 42 << endl; - kout << "Char test: " << 'A' << endl; +static char app_stack[4096]; +Application app(&app_stack[4096]); - kout << "Test -> " << endl; - kout << "zero: " << 0 << " -> 0" << endl; - kout << "decimal: " << dec << 42 << " -> 42" << endl; - kout << "binary: " << bin << 42 << dec << " -> 0b101010" << endl; - kout << "octal: " << oct << 42 << dec << " -> 052" << endl; - kout << "hex: " << hex << 42 << dec << " -> 0x2a" << endl; - kout << "uint64_t max: " << ~((unsigned long)0) << " -> 18446744073709551615" - << endl; - kout << "int64_t max: " << ~(1l << 63) << " -> 9223372036854775807" << endl; - kout << "int64_t min: " << (1l << 63) << " -> -9223372036854775808" << endl; - kout << "some int64_t: " << (-1234567890123456789) - << " -> -1234567890123456789" << endl; - kout << "some int64_t: " << (1234567890123456789) << " -> 1234567890123456789" - << endl; - kout << "pointer: " << reinterpret_cast(1994473406541717165ul) - << " -> 0x1badcafefee1dead" << endl; - kout << "smiley: " << static_cast(1) << endl; - kout.flush(); +Loop loop1('A', 0, 20); +Loop loop2('B', 40, 20); - int speed = 0; - int delay = 0; - - kout << "Use Arrow keys to control repeat rate:" << endl; - kout << "UP/DOWN: Speed (0-31), LEFT/RIGHT: Delay (0-3)" << endl; - kout.flush(); +int main() { + kout.setpos(0, 0); + kout.flush(); - while (true) { - Key k = keyboard.key_hit(); - if (k.valid()) { - bool changed = false; - if (k.scancode() == Key::scan::up) { - if (speed > 0) - speed--; - changed = true; - } else if (k.scancode() == Key::scan::down) { - if (speed < 31) - speed++; - changed = true; - } else if (k.scancode() == Key::scan::left) { - if (delay > 0) - delay--; - changed = true; - } else if (k.scancode() == Key::scan::right) { - if (delay < 3) - delay++; - changed = true; - } + // Initialize keyboard and interrupts + keyboard.plugin(); + cpu.enable_int(); - if (changed) { - keyboard.set_repeat_rate(speed, delay); - kout << endl - << "Repeat rate: Speed=" << speed << ", Delay=" << delay << endl; - } else if (k.ascii() != 0) { - kout << k.ascii(); - } - kout.flush(); - } - } + scheduler.ready(app); + scheduler.ready(loop1); + scheduler.ready(loop2); + scheduler.resume(); - return 0; + return 0; } diff --git a/object/chain.h b/object/chain.h new file mode 100644 index 0000000..f3fd406 --- /dev/null +++ b/object/chain.h @@ -0,0 +1,25 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* C H A I N */ +/* */ +/*---------------------------------------------------------------------------*/ +/* By inheriting from the Chain class, the derived class inherits a chain */ +/* pointer that allows its instances to be enqueued in a linked list */ +/* (implemented in the Queue class). */ +/*****************************************************************************/ + +#ifndef __chain_include__ +#define __chain_include__ + +class Chain { + +public: + Chain(const Chain ©) = delete; // prevent copying + Chain& operator=(const Chain&) = delete; // prevent assignment + Chain() {} + Chain *next; +}; + +#endif diff --git a/object/queue.cc b/object/queue.cc new file mode 100644 index 0000000..03331c1 --- /dev/null +++ b/object/queue.cc @@ -0,0 +1,78 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* Q U E U E */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Implementation of a singly-linked list of Chain objects. enqueue() */ +/* inserts a new element at the end of the list, dequeue() removes the first */ +/* element, and remove() remove the element indicated by its argument. */ +/* Initially, the queue is empty. */ +/* */ +/* The implementation is a little bit tricky (see exercises). tail does not, */ +/* as usual, point to the last element of the list, but to the next pointer */ +/* of this last element (or to the head pointer of the list, if it is still */ +/* empty). This allows us to implement the enqueue() operation without a */ +/* conditional distinguishing the empty from the non-empty case. However, */ +/* when removing elements, we cannot do without this conditional. */ +/*****************************************************************************/ + +#include "object/queue.h" + +// ENQUEUE: Append the Chain object at the end of the list. + +void Queue::enqueue(Chain *item) +{ + item->next = 0; // The new element has no successor yet. + *tail = item; // Append at the end of the list + tail = &(item->next); // and update the tail pointer. +} + +// DEQUEUE: Returns the list's first element and removes it from the list. +// If the list does not contain any elements, it returns a nullptr. + +Chain *Queue::dequeue() +{ + Chain *item; + + item = head; // The head pointer denotes the first element + if (item) { // or contains a nullptr if the list is empty. + head = item->next; // Unlink the first element from the list. + if (!head) // If the list is now empty, we must make sure + tail = &head; // the tail pointer points to head again. + else // Otherwise, invalidate the pointer + item->next = 0;// to this element's successor. + } + return item; +} + +// REMOVE: Searches the supplied Chain element and removes it. + +void Queue::remove(Chain *item) +{ + Chain *cur; + + if (head) { + cur = head; // Search starts at the list's head. + if (item == cur) // If the first element is the sought-after, + dequeue(); // dequeue() suffices for removal. + else { + // Search until we reach the list's end, or the next element is + // the one we are looking for. + while (cur->next && item != cur->next) { + cur = cur->next; + } + + if (cur->next) { + cur->next = item->next; // unlink element + item->next = 0; // invalidate next ptr + + // If cur has no successor, + if (!cur->next) + // we need to update tail. + tail = &(cur->next); + } + } + } +} diff --git a/object/queue.h b/object/queue.h new file mode 100644 index 0000000..485ee66 --- /dev/null +++ b/object/queue.h @@ -0,0 +1,39 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* Q U E U E */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Implementation of a singly-linked list of Chain objects. enqueue() */ +/* inserts a new element at the end of the list, dequeue() removes the first */ +/* element, and remove() remove the element indicated by its argument. */ +/* Initially, the queue is empty. */ +/*****************************************************************************/ + +#ifndef __queue_include__ +#define __queue_include__ + +#include "object/chain.h" + +class Queue { + +protected: + Chain *head; + Chain **tail; + +public: + Queue(const Queue ©) = delete; // prevent copying + Queue& operator=(const Queue&) = delete; // prevent assignment + + Queue() + { + head = 0; + tail = &head; + } + void enqueue(Chain *item); + Chain *dequeue(); + void remove(Chain *); +}; + +#endif diff --git a/startup.asm b/startup.asm index 8c5afda..7fb8d95 100644 --- a/startup.asm +++ b/startup.asm @@ -370,6 +370,7 @@ reprogram_pics: mov al, 0x02 ; ICW3 slave: connected to master's IRQ2 out 0xa1, al call delay + ; set it to mannual mode aka 0x01 support our own testing rig mov al, 0x03 ; ICW4: 8086 mode and automatic EOI out 0x21, al call delay diff --git a/syscall/guarded_scheduler.cc b/syscall/guarded_scheduler.cc new file mode 100644 index 0000000..5bc34e0 --- /dev/null +++ b/syscall/guarded_scheduler.cc @@ -0,0 +1,37 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* G U A R D E D _ S C H E D U L E R */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Implements the system-call interface to the Scheduler. */ +/*****************************************************************************/ + + +/* Add your code here */ +#include "guarded_scheduler.h" +#include "thread/scheduler.h" +#include "thread.h" +#include "guard/secure.h" +void Guarded_Scheduler::ready(Thread &that){ + Secure secure; + Scheduler::ready(that); +} + +void Guarded_Scheduler::exit(){ + Secure secure; + Scheduler::exit(); +} + +void Guarded_Scheduler::kill(Thread& that) +{ + Secure guard; + Scheduler::kill(that); +} + +void Guarded_Scheduler::resume() +{ + Secure guard; + Scheduler::resume(); +} diff --git a/syscall/guarded_scheduler.h b/syscall/guarded_scheduler.h new file mode 100644 index 0000000..f464679 --- /dev/null +++ b/syscall/guarded_scheduler.h @@ -0,0 +1,31 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* G U A R D E D _ S C H E D U L E R */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Implements the system-call interface to the Scheduler. */ +/*****************************************************************************/ + +#ifndef __guarded_scheduler_include__ +#define __guarded_scheduler_include__ + +/* Add your code here */ +#include "thread.h" +#include "thread/scheduler.h" +class Guarded_Scheduler:public Scheduler +/* Add your code here */ +{ +public: + Guarded_Scheduler(const Guarded_Scheduler ©) = delete; // prevent copying + Guarded_Scheduler& operator=(const Guarded_Scheduler&) = delete; // prevent assignment + Guarded_Scheduler () {} + void ready(Thread &that); + void exit(); + void kill(Thread &that); + void resume(); +/* Add your code here */ +}; + +#endif diff --git a/syscall/thread.h b/syscall/thread.h new file mode 100644 index 0000000..1ed0a40 --- /dev/null +++ b/syscall/thread.h @@ -0,0 +1,28 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* T H R E A D */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Implements user interface of a thread. */ +/*****************************************************************************/ + +#ifndef __thread_include__ +#define __thread_include__ + +/* Add your code here */ +#include "thread/entrant.h" +class Thread: public Entrant +/* Add your code here */ + +{ +public: + Thread(const Thread ©) = delete; // prevent copying + Thread& operator=(const Thread&) = delete; // prevent assignment +/* Add your code here */ + Thread(void *tos): Entrant(tos){} + +}; + +#endif diff --git a/thread/coroutine.cc b/thread/coroutine.cc new file mode 100644 index 0000000..06ebb9f --- /dev/null +++ b/thread/coroutine.cc @@ -0,0 +1,41 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* C O R O U T I N E */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Represents the abstraction of a coroutine. */ +/* In the constructor, we set up the initial context of the coroutine. go() */ +/* activates the first coroutine. All further coroutine switches should be */ +/* initiated by resume(). */ +/* To be able to save the context at a couroutine switch, every Coroutine */ +/* object contains a struct toc that stores the values of all non-volatile */ +/* registers. */ +/*****************************************************************************/ + +#include "thread/coroutine.h" +#include "thread/kickoff.h" + +// Functions that are implemented at C or assembler level must be declared as +// extern "C", because they do not conform to C++ name mangling. +extern "C" { + void toc_settle(struct toc *regs, void *tos, + void (*kickoff)(void *, void *, void *, void *, void *, void *, + void *), + void *object); + void toc_go(struct toc* regs); + void toc_switch(struct toc* regs_now, struct toc* reg_then); +} + +Coroutine::Coroutine(void* tos) { + toc_settle(®s, tos, (void (*)(void*, void*, void*, void*, void*, void*, void*)) kickoff, this); +} + +void Coroutine::go() { + toc_go(®s); +} + +void Coroutine::resume(Coroutine& next) { + toc_switch(®s, &next.regs); +} diff --git a/thread/coroutine.h b/thread/coroutine.h new file mode 100644 index 0000000..d257d8f --- /dev/null +++ b/thread/coroutine.h @@ -0,0 +1,35 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* C O R O U T I N E */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Represents the abstraction of a coroutine. */ +/* In the constructor, we set up the initial context of the coroutine. go() */ +/* activates the first coroutine. All further coroutine switches should be */ +/* initiated by resume(). */ +/* To be able to save the context at a couroutine switch, every Coroutine */ +/* object contains a struct toc that stores the values of all non-volatile */ +/* registers. */ +/*****************************************************************************/ + +#ifndef __Coroutine_include__ +#define __Coroutine_include__ + +#include "machine/toc.h" + +class Coroutine { +private: + struct toc regs; +public: + Coroutine(const Coroutine ©) = delete; // prevent copying + Coroutine& operator=(const Coroutine&) = delete; // prevent assignment + + Coroutine(void* tos); + virtual void action() = 0; + void go(); + void resume(Coroutine& next); +}; + +#endif diff --git a/thread/dispatch.cc b/thread/dispatch.cc new file mode 100644 index 0000000..833b1b5 --- /dev/null +++ b/thread/dispatch.cc @@ -0,0 +1,30 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* D I S P A T C H E R */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Dispatcher implementation. */ +/* The Dispatcher maintains the life pointer that points to the currently */ +/* active coroutine. go() initializes the life pointer and starts the first */ +/* coroutine, all further context switches are triggered by dispatch(). */ +/* active() returns the life pointer. */ +/*****************************************************************************/ + +#include "thread/dispatch.h" + +void Dispatcher::go(Coroutine& first) { + life = &first; + first.go(); +} + +void Dispatcher::dispatch(Coroutine& next) { + Coroutine* previous = life; + life = &next; + previous->resume(next); +} + +Coroutine* Dispatcher::active() { + return life; +} diff --git a/thread/dispatch.h b/thread/dispatch.h new file mode 100644 index 0000000..4c5de15 --- /dev/null +++ b/thread/dispatch.h @@ -0,0 +1,34 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* D I S P A T C H E R */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Dispatcher implementation. */ +/* The Dispatcher maintains the life pointer that points to the currently */ +/* active coroutine. go() initializes the life pointer and starts the first */ +/* coroutine, all further context switches are triggered by dispatch(). */ +/* active() returns the life pointer. */ +/*****************************************************************************/ + +#ifndef __dispatch_include__ +#define __dispatch_include__ + +#include "thread/coroutine.h" + +class Dispatcher { +private: + Coroutine* life; +public: + Dispatcher(const Dispatcher ©) = delete; // prevent copying + Dispatcher& operator=(const Dispatcher&) = delete; // prevent assignment + + Dispatcher() : life(nullptr) {} + + void go(Coroutine& first); + void dispatch(Coroutine& next); + Coroutine* active(); +}; + +#endif diff --git a/thread/entrant.h b/thread/entrant.h new file mode 100644 index 0000000..ec75227 --- /dev/null +++ b/thread/entrant.h @@ -0,0 +1,25 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* E N T R A N T */ +/* */ +/*---------------------------------------------------------------------------*/ +/* A coroutine that is managed by the Scheduler. */ +/*****************************************************************************/ + +#ifndef __entrant_include__ +#define __entrant_include__ + +#include "thread/coroutine.h" +#include "object/chain.h" + +class Entrant : public Coroutine, public Chain { +public: + Entrant(const Entrant ©) = delete; // prevent copying + Entrant& operator=(const Entrant&) = delete; // prevent assignment + + Entrant(void* tos) : Coroutine(tos) {} +}; + +#endif diff --git a/thread/kickoff.cc b/thread/kickoff.cc new file mode 100644 index 0000000..92f2214 --- /dev/null +++ b/thread/kickoff.cc @@ -0,0 +1,24 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* K I C K O F F */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Function to start a coroutine. */ +/* Since this function is not really called, but only jumped to by a clever */ +/* initialization of the stack of the coroutine, it must never terminate. */ +/* Otherwise a meaningless value would be interpreted as a return address */ +/* and the computer would crash. */ +/*****************************************************************************/ + +#include "thread/kickoff.h" +#include "thread/coroutine.h" +#include "thread/scheduler.h" + +extern Scheduler scheduler; + +void kickoff(void* dummy1, void* dummy2, void* dummy3, void* dummy4, void* dummy5, void* dummy6, Coroutine* object) { + object->action(); + scheduler.exit(); +} diff --git a/thread/kickoff.h b/thread/kickoff.h new file mode 100644 index 0000000..5d31e43 --- /dev/null +++ b/thread/kickoff.h @@ -0,0 +1,22 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* K I C K O F F */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Function to start a coroutine. */ +/* Since this function is not really called, but only jumped to by a clever */ +/* initialization of the stack of the coroutine, it must never terminate. */ +/* Otherwise a meaningless value would be interpreted as a return address */ +/* and the computer would crash. */ +/*****************************************************************************/ + +#ifndef __kickoff_include__ +#define __kickoff_include__ + +class Coroutine; + +void kickoff(void* dummy1, void* dummy2, void* dummy3, void* dummy4, void* dummy5, void* dummy6, Coroutine* object); + +#endif diff --git a/thread/scheduler.cc b/thread/scheduler.cc new file mode 100644 index 0000000..0b96592 --- /dev/null +++ b/thread/scheduler.cc @@ -0,0 +1,56 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* S C H E D U L E R */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Scheduler implementation. */ +/*****************************************************************************/ + +#include "thread/scheduler.h" +#include "machine/cpu.h" + +extern CPU cpu; + +void Scheduler::ready(Entrant& that) { + ready_queue.enqueue(&that); +} + +void Scheduler::schedule() { + Entrant* next = (Entrant*)ready_queue.dequeue(); + if (next) { + Entrant* current = (Entrant*)active(); + if (current) { + ready_queue.enqueue(current); + } + dispatch(*next); + } +} + +void Scheduler::exit() { + Entrant* next = (Entrant*)ready_queue.dequeue(); + if (next) { + dispatch(*next); + } else { + cpu.disable_int(); + while (true) { + cpu.halt(); + } + } +} + +void Scheduler::kill(Entrant& that) { + if (active() == &that) { + exit(); + } else { + ready_queue.remove(&that); + } +} + +void Scheduler::resume() { + Entrant* next = (Entrant*)ready_queue.dequeue(); + if (next) { + go(*next); + } +} diff --git a/thread/scheduler.h b/thread/scheduler.h new file mode 100644 index 0000000..9a7381c --- /dev/null +++ b/thread/scheduler.h @@ -0,0 +1,34 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* S C H E D U L E R */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Scheduler implementation. */ +/*****************************************************************************/ + +#ifndef __schedule_include__ +#define __schedule_include__ + +#include "thread/dispatch.h" +#include "object/queue.h" +#include "thread/entrant.h" + +class Scheduler : public Dispatcher { +private: + Queue ready_queue; +public: + Scheduler (const Scheduler ©) = delete; // prevent copying + Scheduler& operator=(const Scheduler&) = delete; // prevent assignment + + Scheduler() {} + + void ready(Entrant& that); + void schedule(); + void exit(); + void kill(Entrant& that); + void resume(); +}; + +#endif diff --git a/user/appl.cc b/user/appl.cc index 5afb65e..ce45a31 100644 --- a/user/appl.cc +++ b/user/appl.cc @@ -8,19 +8,53 @@ /* The Application class defines the (only) application for OOStuBS. */ /*****************************************************************************/ -/* INCLUDES */ - #include "user/appl.h" #include "device/cgastr.h" -/* Add your code here */ - -/* GLOBAL VARIABLES */ +#include "machine/cpu.h" +#include "guard/secure.h" +#include "syscall/guarded_scheduler.h" +extern CPU cpu; extern CGA_Stream kout; -/* Add your code here */ - +extern Guarded_Scheduler scheduler; + void Application::action() { -/* Add your code here */ - + kout << "Helix OS 2.0.0 - Task 4" << endl; + kout << "Hello O_Stream!" << endl; + kout << "Number: " << 42 << endl; + kout << "Char test: " << 'A' << endl; + + kout << "Test -> " << endl; + kout << "zero: " << 0 << " -> 0" << endl; + kout << "decimal: " << dec << 42 << " -> 42" << endl; + kout << "binary: " << bin << 42 << dec << " -> 0b101010" << endl; + kout << "octal: " << oct << 42 << dec << " -> 052" << endl; + kout << "hex: " << hex << 42 << dec << " -> 0x2a" << endl; + kout << "uint64_t max: " << ~((unsigned long)0) << " -> 18446744073709551615" + << endl; + kout << "int64_t max: " << ~(1l << 63) << " -> 9223372036854775807" << endl; + kout << "int64_t min: " << (1l << 63) << " -> -9223372036854775808" << endl; + kout << "some int64_t: " << (-1234567890123456789) + << " -> -1234567890123456789" << endl; + kout << "some int64_t: " << (1234567890123456789) << " -> 1234567890123456789" + << endl; + kout << "pointer: " << reinterpret_cast(1994473406541717165ul) + << " -> 0x1badcafefee1dead" << endl; + kout << "smiley: " << static_cast(1) << endl; + kout.flush(); + + int counter = 0; + while (true) { + { + Secure secure; + int x, y; + kout.getpos(x, y); + kout.setpos(0, 24); + kout << "Counter: " << counter++ << " "; + kout.flush(); + kout.setpos(x, y); + } + scheduler.schedule(); + } } diff --git a/user/appl.h b/user/appl.h index b6b8330..3b4d6c0 100644 --- a/user/appl.h +++ b/user/appl.h @@ -11,14 +11,14 @@ #ifndef __application_include__ #define __application_include__ -class Application - -{ +#include "thread/entrant.h" +class Application : public Entrant +{ public: Application (const Application ©) = delete; // prevent copying Application& operator=(const Application&) = delete; // prevent assignment -/* Add your code here */ + Application(void* tos) : Entrant(tos) {} void action (); }; diff --git a/user/loop.cc b/user/loop.cc new file mode 100644 index 0000000..0be2690 --- /dev/null +++ b/user/loop.cc @@ -0,0 +1,38 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* L O O P */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Loop is a thread that does nothing else but count upwards and print this */ +/* on the screen. In between, it yields the CPU. The Scheduler then decides */ +/* which thread shall run next. */ +/*****************************************************************************/ + +#include "user/loop.h" +#include "device/cgastr.h" +#include "thread/scheduler.h" +#include "guard/secure.h" +#include + +extern CGA_Stream kout; +extern Scheduler scheduler; + +Loop::Loop(char id, int pos_x, int pos_y) + : Entrant(&stack[4096]), pos_x(pos_x), pos_y(pos_y), id(id) {} + +void Loop::action() { + for (int i = 0; i < INT64_MAX; i++) { + { + Secure secure; + int x, y; + kout.getpos(x, y); + kout.setpos(pos_x, pos_y); + kout << id << ": " << i << " "; + kout.flush(); + kout.setpos(x, y); + } + scheduler.schedule(); + } +} diff --git a/user/loop.h b/user/loop.h new file mode 100644 index 0000000..7494844 --- /dev/null +++ b/user/loop.h @@ -0,0 +1,27 @@ +/*****************************************************************************/ +/* Operating-System Construction */ +/*---------------------------------------------------------------------------*/ +/* */ +/* L O O P */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Loop is a thread that does nothing else but count upwards and print this */ +/* on the screen. In between, it yields the CPU. The Scheduler then decides */ +/* which thread shall run next. */ +/*****************************************************************************/ + +#ifndef __loop_include__ +#define __loop_include__ + +#include "thread/entrant.h" + +class Loop : public Entrant { +private: + char stack[4096]; + int pos_x, pos_y; + char id; +public: + Loop(char id, int pos_x, int pos_y); + void action(); +}; +#endif