add clock
This commit is contained in:
		
							parent
							
								
									86b343f171
								
							
						
					
					
						commit
						b1cf9e5dfb
					
				
					 23 changed files with 643 additions and 15 deletions
				
			
		
							
								
								
									
										12
									
								
								applications/clock/makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								applications/clock/makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| SOURCES = \
 | ||||
|   main.cpp | ||||
| 
 | ||||
| build/%.cpp.o: source/%.cpp | ||||
| 	@mkdir -p $(@D) | ||||
| 	$(HILBERT_CC) -c $^ -o $@ | ||||
| 
 | ||||
| build/clock.elf: $(SOURCES:%=build/%.o) | ||||
| 	$(HILBERT_CC) $^ -ldaguerre -lpake -o $@ | ||||
| 
 | ||||
| clean: | ||||
| 	rm -rf build | ||||
							
								
								
									
										63
									
								
								applications/clock/source/main.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								applications/clock/source/main.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,63 @@ | |||
| #include <pake/widgets/fixed-text.hpp> | ||||
| #include <daguerre/psf.hpp> | ||||
| #include <pake/window.hpp> | ||||
| #include <ctime> | ||||
| 
 | ||||
| static daguerre::fixed_font<bool> *font; | ||||
| 
 | ||||
| std::string the_time() { | ||||
| 
 | ||||
|   time_t t = time(0); | ||||
| 
 | ||||
|   //convert to edt - TODO: timezones in euler
 | ||||
|   t -= 4 * 3600 * 1024; | ||||
| 
 | ||||
|   tm *gt = gmtime(&t); | ||||
| 
 | ||||
|   int hour = (gt->tm_hour - 1) % 12 + 1; | ||||
|   int min = gt->tm_min; | ||||
|   int sec = gt->tm_sec; | ||||
|   bool pm = gt->tm_hour >= 12; | ||||
| 
 | ||||
|   std::string s; | ||||
|   s.resize(8); | ||||
| 
 | ||||
|   s[0] = hour / 10 + '0'; s[1] = hour % 10 + '0'; | ||||
|   s[3] = min  / 10 + '0'; s[4] = min  % 10 + '0'; | ||||
| 
 | ||||
|   s[2] = sec % 2 == 0 ? ':' : ' '; | ||||
| 
 | ||||
|   s[5] = ' '; | ||||
|   s[6] = pm ? 'p' : 'a'; | ||||
|   s[7] = 'm'; | ||||
| 
 | ||||
|   if (s[0] == '0') | ||||
|     s.erase(0, 1); | ||||
| 
 | ||||
|   return s; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| int main(int, char **) { | ||||
| 
 | ||||
|   font = new daguerre::fixed_font<bool>( | ||||
|     daguerre::try_load_psf("/assets/terminus/10x18-bold.psf").value()); | ||||
| 
 | ||||
|   pake::widgets::fixed_text *text = | ||||
|     new pake::widgets::fixed_text(the_time(), font, | ||||
|       euler::syscall::encode_color(0xaa, 0xaa, 0xaa), | ||||
|       euler::syscall::encode_color(0x00, 0x00, 0x00), | ||||
|       pake::halign::center, pake::valign::center); | ||||
| 
 | ||||
|   pake::window w(90, 28, "Clock"); | ||||
|   w.set_root(std::unique_ptr<pake::widget>(text)); | ||||
|   w.render_and_send_to_compositor(); | ||||
|   w.show(); | ||||
| 
 | ||||
|   while (1) { | ||||
|     euler::syscall::sleep(512); | ||||
|     text->set_text(the_time()); | ||||
|     w.render_and_send_to_compositor(); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  | @ -6,6 +6,6 @@ | |||
| int main(int, char **) { | ||||
|   euler::syscall::process_handle dummy; | ||||
|   euler::syscall::start_process("/bin/compositor", {}, {}, dummy); | ||||
|   euler::syscall::start_process("/bin/hello", {}, {}, dummy); | ||||
|   euler::syscall::start_process("/bin/clock", {}, {}, dummy); | ||||
|   return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -4,6 +4,8 @@ rcx, rflags, r8-r11 are clobbered. | |||
| 
 | ||||
| interrupts (including the timer!) are disabled during system calls. | ||||
| 
 | ||||
| a "mibisecond" is a 1024th of a second | ||||
| 
 | ||||
| stream result: | ||||
|    0 = success | ||||
|    1 = bad handle | ||||
|  | @ -203,3 +205,11 @@ set thread name: | |||
|   rax in: 24 | ||||
|   rdi in: pointer to thread name | ||||
|   rsi in: thread name length | ||||
| 
 | ||||
| sleep: | ||||
|   rax in: 25 | ||||
|   rdi in: "mibiseconds" to sleep for | ||||
| 
 | ||||
| get time: | ||||
|   rax in: 26 | ||||
|   rax out: "mibiseconds" since january 1st 2000 | ||||
|  |  | |||
							
								
								
									
										20
									
								
								euler/include/ctime
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								euler/include/ctime
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <cstdint> | ||||
| 
 | ||||
| typedef uint64_t time_t; | ||||
| 
 | ||||
| struct tm { | ||||
|   int tm_sec; | ||||
|   int tm_min; | ||||
|   int tm_hour; | ||||
|   int tm_mday; | ||||
|   int tm_mon; | ||||
|   int tm_year; | ||||
|   int tm_wday; | ||||
|   int tm_yday; | ||||
|   int tm_isdst; | ||||
| }; | ||||
| 
 | ||||
| time_t time(time_t *arg); | ||||
| tm *gmtime(const time_t *time); | ||||
|  | @ -159,6 +159,11 @@ namespace euler::syscall { | |||
| 
 | ||||
|   void set_thread_name(const std::string &name); | ||||
| 
 | ||||
|   void sleep(uint64_t mibiseconds); | ||||
| 
 | ||||
|   //out is mibiseconds since january 1st 2000
 | ||||
|   uint64_t get_time(); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #include <string> | ||||
|  |  | |||
|  | @ -12,6 +12,8 @@ namespace std { | |||
|     std::vector<char> characters; | ||||
| 
 | ||||
|   public: | ||||
|     static const size_t npos = (size_t)-1; | ||||
| 
 | ||||
|     constexpr string() : characters({'\0'}) {} | ||||
| 
 | ||||
|     constexpr string(const string &other) | ||||
|  | @ -74,6 +76,14 @@ namespace std { | |||
|       return characters[pos]; | ||||
|     } | ||||
| 
 | ||||
|     constexpr string &erase(size_t index = 0, size_t count = npos) { | ||||
|       count = std::min(count, size() - index); | ||||
|       for (size_t i = index; i + count < size(); ++i) | ||||
|         characters[i] = characters[i + count]; | ||||
|       resize(size() - count); | ||||
|       return *this; | ||||
|     } | ||||
| 
 | ||||
|   }; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| LIBC_SOURCES = \
 | ||||
|   entry.cpp std/string.cpp std/cstring.cpp syscall.cpp std/cstdlib.cpp \
 | ||||
|   heap.cpp syscall.asm std/cctype.cpp std/cstdio.cpp stream.cpp | ||||
|   heap.cpp syscall.asm std/cctype.cpp std/cstdio.cpp stream.cpp std/ctime.cpp | ||||
| 
 | ||||
| clean: | ||||
| 	rm -rf build | ||||
|  |  | |||
							
								
								
									
										58
									
								
								euler/source/std/ctime.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								euler/source/std/ctime.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,58 @@ | |||
| #include <euler/syscall.hpp> | ||||
| #include <ctime> | ||||
| 
 | ||||
| time_t time(time_t *arg) { | ||||
|   time_t t = euler::syscall::get_time(); | ||||
|   if (arg) *arg = t; | ||||
|   return t; | ||||
| } | ||||
| 
 | ||||
| static tm static_tm; | ||||
| 
 | ||||
| static int days_per_month[] = { | ||||
|   31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, | ||||
|   31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, | ||||
|   31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, | ||||
|   31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 | ||||
| }; | ||||
| 
 | ||||
| tm *gmtime(const time_t *time) { | ||||
| 
 | ||||
|   time_t t = *time / 1024; | ||||
| 
 | ||||
|   static_tm.tm_isdst = 0; | ||||
| 
 | ||||
|   static_tm.tm_sec  = t % 60; t /= 60; | ||||
|   static_tm.tm_min  = t % 60; t /= 60; | ||||
|   static_tm.tm_hour = t % 24; t /= 24; | ||||
|   static_tm.tm_wday = (t + 5) % 7 + 1; | ||||
|   static_tm.tm_year = (t / 1461) * 4 + 100; | ||||
| 
 | ||||
|   int days_into_quadyear = t % 1461; | ||||
| 
 | ||||
|   static_tm.tm_yday = 0; | ||||
|   static_tm.tm_mon = 0; | ||||
|   static_tm.tm_mday = 1; | ||||
| 
 | ||||
|   for (int i = 0; i < 48; ++i) { | ||||
|     if (days_into_quadyear >= days_per_month[i]) { | ||||
|       days_into_quadyear -= days_per_month[i]; | ||||
|       if (static_tm.tm_mon == 11) { | ||||
|         static_tm.tm_mon = 0; | ||||
|         static_tm.tm_yday = 0; | ||||
|       } | ||||
|       else { | ||||
|         ++static_tm.tm_mon; | ||||
|         static_tm.tm_yday += days_per_month[i]; | ||||
|       } | ||||
|     } | ||||
|     else { | ||||
|       static_tm.tm_yday += days_into_quadyear; | ||||
|       static_tm.tm_mday += days_into_quadyear; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   return &static_tm; | ||||
| 
 | ||||
| } | ||||
|  | @ -407,4 +407,27 @@ namespace euler::syscall { | |||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   void sleep(uint64_t mibiseconds) { | ||||
| 
 | ||||
|     uint64_t rax = 25; | ||||
|     uint64_t rdi = mibiseconds; | ||||
|     uint64_t rsi; | ||||
|     uint64_t rdx; | ||||
| 
 | ||||
|     __euler_do_syscall(rax, rdi, rsi, rdx); | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   uint64_t get_time() { | ||||
| 
 | ||||
|     uint64_t rax = 26; | ||||
|     uint64_t rdi; | ||||
|     uint64_t rsi; | ||||
|     uint64_t rdx; | ||||
| 
 | ||||
|     __euler_do_syscall(rax, rdi, rsi, rdx); | ||||
|     return rax; | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
							
								
								
									
										18
									
								
								kernel/include/hilbert/kernel/timer.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								kernel/include/hilbert/kernel/timer.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <hilbert/kernel/application.hpp> | ||||
| 
 | ||||
| namespace hilbert::kernel::timer { | ||||
| 
 | ||||
|   void init_timer(); | ||||
| 
 | ||||
|   //"mibiseconds" (1 second / 1024) since january 1st 2000
 | ||||
|   extern uint64_t current_time; | ||||
| 
 | ||||
|   //when current_time >= sleeping_until, puts thread into paused threads
 | ||||
|   void register_sleeping_thread( | ||||
|     uint64_t sleeping_until, application::thread *thread); | ||||
| 
 | ||||
|   void on_timer_interrupt(); | ||||
| 
 | ||||
| } | ||||
|  | @ -126,6 +126,25 @@ namespace hilbert::kernel::utility { | |||
|       last = n; | ||||
|     } | ||||
| 
 | ||||
|     //if other == 0, then insert at the end
 | ||||
|     void insert_before(value_t &&value, node *other) { | ||||
|       node *n = new node {}; | ||||
|       n->value = value; | ||||
|       n->next = other; | ||||
|       if (other) { | ||||
|         n->prev = other->prev; | ||||
|         other->prev = n; | ||||
|       } | ||||
|       else { | ||||
|         n->prev = last; | ||||
|         last = n; | ||||
|       } | ||||
|       if (n->prev) | ||||
|         n->prev->next = n; | ||||
|       else | ||||
|         first = n; | ||||
|     } | ||||
| 
 | ||||
|     void clear() { | ||||
|       if (first) { | ||||
|         for (node *n = first->next; n; n = n->next) | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ SOURCES = \ | |||
|   storage/bd/memory.cpp storage/fs/tarfs.cpp application.asm application.cpp \
 | ||||
|   framebuffer.cpp interrupts.asm interrupts.cpp allocator.cpp storage.cpp \
 | ||||
|   syscall.cpp utility.cpp paging.asm paging.cpp entry.cpp input.cpp panic.cpp \
 | ||||
|   vfile.cpp serial.asm app-memory.cpp load-app.cpp | ||||
|   vfile.cpp serial.asm app-memory.cpp load-app.cpp timer.cpp timer.asm | ||||
| 
 | ||||
| build/%.asm.o: source/%.asm | ||||
| 	@mkdir -p $(@D) | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| #include <hilbert/kernel/serial.hpp> | ||||
| #include <hilbert/kernel/input.hpp> | ||||
| #include <hilbert/kernel/panic.hpp> | ||||
| #include <hilbert/kernel/timer.hpp> | ||||
| #include <hilbert/kernel/vfile.hpp> | ||||
| #include <limine.h> | ||||
| 
 | ||||
|  | @ -179,6 +180,7 @@ extern "C" [[noreturn]] void entry() { | |||
|   if (!have_initfs) | ||||
|     panic(0x5f8860); | ||||
| 
 | ||||
|   timer::init_timer(); | ||||
|   input::init_input(); | ||||
|   application::init_applications(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -267,6 +267,22 @@ isr_end: | |||
| 
 | ||||
|   ret | ||||
| 
 | ||||
| extern on_rtc_interrupt | ||||
| 
 | ||||
| rtc_isr: | ||||
| 
 | ||||
|   call isr_start | ||||
| 
 | ||||
|   call on_rtc_interrupt | ||||
| 
 | ||||
|   mov al, 0x20 | ||||
|   out 0x20, al | ||||
|   out 0xa0, al | ||||
| 
 | ||||
|   call isr_end | ||||
| 
 | ||||
|   iretq | ||||
| 
 | ||||
| extern on_keyboard_interrupt | ||||
| 
 | ||||
| keyboard_isr: | ||||
|  | @ -343,7 +359,7 @@ load_gdt_and_idt: | |||
|   out 0x21, al | ||||
|   mov al, 0x01 | ||||
|   out 0x21, al | ||||
|   mov al, 0xf9 ;mask all but irq 1 and 2 | ||||
|   mov al, 0xf9 ;mask all but irqs 1 and 2 | ||||
|   out 0x21, al | ||||
| 
 | ||||
|   mov al, 0x11 | ||||
|  | @ -354,9 +370,15 @@ load_gdt_and_idt: | |||
|   out 0xa1, al | ||||
|   mov al, 0x01 | ||||
|   out 0xa1, al | ||||
|   mov al, 0xef ;mask all but irq 12 | ||||
|   mov al, 0xee ;mask all but irqs 8 and 12 | ||||
|   out 0xa1, al | ||||
| 
 | ||||
|   ;register rtc interrupt | ||||
| 
 | ||||
|   mov rdi, 0x28 | ||||
|   mov rsi, rtc_isr | ||||
|   call set_isr | ||||
| 
 | ||||
|   ;register keyboard and mouse interrupts | ||||
| 
 | ||||
|   mov rdi, 0x21 | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| #include <hilbert/kernel/paging.hpp> | ||||
| #include <hilbert/kernel/input.hpp> | ||||
| #include <hilbert/kernel/panic.hpp> | ||||
| #include <hilbert/kernel/timer.hpp> | ||||
| #include <hilbert/kernel/vfile.hpp> | ||||
| 
 | ||||
| namespace hilbert::kernel::syscall { | ||||
|  | @ -800,6 +801,29 @@ namespace hilbert::kernel::syscall { | |||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   void sleep_syscall( | ||||
|     uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) { | ||||
| 
 | ||||
|     uint64_t mis = rdi; | ||||
|     set_zero(rax, rdi, rsi, rdx); | ||||
| 
 | ||||
|     auto *t = application::running_thread; | ||||
| 
 | ||||
|     timer::register_sleeping_thread( | ||||
|       timer::current_time + mis, t); | ||||
| 
 | ||||
|     application::yield(t->saved_state); | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   void get_time_syscall( | ||||
|     uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) { | ||||
| 
 | ||||
|     set_zero(rax, rdi, rsi, rdx); | ||||
|     rax = timer::current_time; | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   void (*handlers[])( | ||||
|     uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) = { | ||||
| 
 | ||||
|  | @ -827,11 +851,13 @@ namespace hilbert::kernel::syscall { | |||
|     &clear_socket_read_queue_syscall, | ||||
|     &get_environment_variable_length_syscall, | ||||
|     &get_environment_variable_value_syscall, | ||||
|     &set_thread_name_syscall | ||||
|     &set_thread_name_syscall, | ||||
|     &sleep_syscall, | ||||
|     &get_time_syscall | ||||
| 
 | ||||
|   }; | ||||
| 
 | ||||
|   static constexpr int max_syscall_number = 24; | ||||
|   static constexpr int max_syscall_number = 26; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										162
									
								
								kernel/source/timer.asm
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								kernel/source/timer.asm
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,162 @@ | |||
| bits 64 | ||||
| 
 | ||||
| section .rodata | ||||
| 
 | ||||
| section .text | ||||
| 
 | ||||
| read_cmos_byte: | ||||
| ;dil in = register number | ||||
| ;al out = value | ||||
| ;rdx and rcx are not touched | ||||
| 
 | ||||
|   mov al, dil | ||||
|   out 0x70, al | ||||
|   in al, 0x71 | ||||
|   ret | ||||
| 
 | ||||
| global enable_rtc_interrupts | ||||
| enable_rtc_interrupts: | ||||
| 
 | ||||
|   ;secondary status register | ||||
|   mov dil, 11 | ||||
|   call read_cmos_byte | ||||
| 
 | ||||
|   ;enable interrupts | ||||
|   or al, 0x40 | ||||
|   mov cl, al | ||||
| 
 | ||||
|   ;do cmos write | ||||
|   mov al, 11 | ||||
|   out 0x70, al | ||||
|   mov al, cl | ||||
|   out 0x71, al | ||||
| 
 | ||||
|   ret | ||||
| 
 | ||||
| global acknowledge_rtc_interrupt | ||||
| acknowledge_rtc_interrupt: | ||||
|   mov dil, 12 | ||||
|   jmp read_cmos_byte | ||||
| 
 | ||||
| convert_bcd: | ||||
| ;al in = byte (possibly bcd) | ||||
| ;al out = byte (not bcd) | ||||
| ;sil 0x02 = bcd | ||||
| ;does not touch rdx, rcx, sil | ||||
|   test sil, 0x02 | ||||
|   jz .no_convert | ||||
| 
 | ||||
|   mov dil, al | ||||
|   and dil, 0xf0 | ||||
|   and al, 0x0f | ||||
|   shr dil, 3 | ||||
|   add al, dil | ||||
|   shl dil, 2 | ||||
|   add al, dil | ||||
| 
 | ||||
| .no_convert: | ||||
|   ret | ||||
| 
 | ||||
| convert_hours: | ||||
| ;al in = byte (possibly bcd and 12 hour) | ||||
| ;al out = byte (not bcd or 12 hour) | ||||
| ;sil 0x02 = bcd, sil 0x01 = 12 hour | ||||
| ;does not touch rdx, rcx, sil | ||||
|   test sil, 0x01 | ||||
|   jz convert_bcd | ||||
| 
 | ||||
|   test al, 0x80 | ||||
|   jz .am | ||||
| 
 | ||||
|   and al, 0x7f | ||||
|   call convert_bcd | ||||
|   cmp al, 12 | ||||
|   je .noon | ||||
| 
 | ||||
|   add al, 12 | ||||
|   ret | ||||
| 
 | ||||
| .noon: | ||||
|   ret | ||||
| 
 | ||||
| .am: | ||||
|   call convert_bcd | ||||
|   cmp al, 12 | ||||
|   je .midnight | ||||
| 
 | ||||
|   ret | ||||
| 
 | ||||
| .midnight: | ||||
|   xor al, al | ||||
|   ret | ||||
| 
 | ||||
| global get_time_from_rtc | ||||
| get_time_from_rtc: | ||||
| ;rax out = time (see timer.cpp for encoding) | ||||
| ;we assume the year is 20xx (sorry) | ||||
| 
 | ||||
|   mov dil, 11 | ||||
|   call read_cmos_byte | ||||
| 
 | ||||
|   shr al, 1 | ||||
|   not al | ||||
|   and al, 3 | ||||
|   mov sil, al | ||||
| 
 | ||||
|   xor rdx, rdx | ||||
| 
 | ||||
| .outer_loop: | ||||
|   mov rcx, rdx | ||||
| 
 | ||||
| .wait_for_update_loop: | ||||
|   ;status register - 0x80 is update in progress | ||||
|   mov dil, 10 | ||||
|   call read_cmos_byte | ||||
|   test al, 0x80 | ||||
|   jnz .wait_for_update_loop | ||||
| 
 | ||||
|   ;years | ||||
|   mov dil, 9 | ||||
|   call read_cmos_byte | ||||
|   call convert_bcd | ||||
|   mov dh, al | ||||
| 
 | ||||
|   ;months | ||||
|   mov dil, 8 | ||||
|   call read_cmos_byte | ||||
|   call convert_bcd | ||||
|   mov dl, al | ||||
| 
 | ||||
|   shl edx, 16 | ||||
| 
 | ||||
|   ;days | ||||
|   mov dil, 7 | ||||
|   call read_cmos_byte | ||||
|   call convert_bcd | ||||
|   mov dh, al | ||||
| 
 | ||||
|   ;hours | ||||
|   mov dil, 4 | ||||
|   call read_cmos_byte | ||||
|   call convert_hours | ||||
|   mov dl, al | ||||
| 
 | ||||
|   shl rdx, 16 | ||||
| 
 | ||||
|   ;minutes | ||||
|   mov dil, 2 | ||||
|   call read_cmos_byte | ||||
|   call convert_bcd | ||||
|   mov dh, al | ||||
| 
 | ||||
|   ;seconds | ||||
|   xor dil, dil | ||||
|   call read_cmos_byte | ||||
|   call convert_bcd | ||||
|   mov dl, al | ||||
| 
 | ||||
|   cmp rdx, rcx | ||||
|   jne .outer_loop | ||||
| 
 | ||||
|   mov rax, rdx | ||||
|   ret | ||||
							
								
								
									
										117
									
								
								kernel/source/timer.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								kernel/source/timer.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,117 @@ | |||
| #include <hilbert/kernel/timer.hpp> | ||||
| 
 | ||||
| namespace hilbert::kernel::timer { | ||||
| 
 | ||||
|   struct sleeping_thread { | ||||
|     uint64_t sleeping_until; | ||||
|     application::thread *thread; | ||||
|   }; | ||||
| 
 | ||||
|   //sorted ascending by sleeping_until
 | ||||
|   static utility::list<sleeping_thread> *sleeping_threads; | ||||
| 
 | ||||
|   uint64_t current_time; | ||||
| 
 | ||||
|   //output is
 | ||||
|   //   seconds     | (minutes <<  8) | (hours << 16) |
 | ||||
|   //  (days << 24) | ( months << 32) | (years << 40)
 | ||||
|   extern "C" uint64_t get_time_from_rtc(); | ||||
| 
 | ||||
|   //index is (year % 4) * 12 + month - 1;
 | ||||
|   //output is days from january 1st 2000 to [month] 1st [year]
 | ||||
|   static uint16_t month_table[] { | ||||
|        0,   31,   60,   91,  121,  152, | ||||
|      182,  213,  244,  274,  305,  335, | ||||
|      366,  397,  425,  456,  486,  517, | ||||
|      547,  578,  609,  639,  670,  700, | ||||
|      731,  762,  790,  821,  851,  882, | ||||
|      912,  943,  974, 1004, 1035, 1065, | ||||
|     1096, 1127, 1155, 1186, 1216, 1247, | ||||
|     1277, 1308, 1339, 1369, 1400, 1430 | ||||
|   }; | ||||
| 
 | ||||
|   //index is (year % 4) * 12 + month - 1;
 | ||||
|   //output is days in that month
 | ||||
|   static uint8_t month_table_2[] = { | ||||
|     31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, | ||||
|     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, | ||||
|     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, | ||||
|     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 | ||||
|   }; | ||||
| 
 | ||||
|   void clamp(uint8_t &value, uint8_t min, uint8_t max) { | ||||
|     if (value < min) | ||||
|       value = min; | ||||
|     else if (value > max) | ||||
|       value = max; | ||||
|   } | ||||
| 
 | ||||
|   extern "C" void enable_rtc_interrupts(); | ||||
| 
 | ||||
|   void init_timer() { | ||||
| 
 | ||||
|     sleeping_threads = new utility::list<sleeping_thread>(); | ||||
| 
 | ||||
|     uint64_t rtc_time = get_time_from_rtc(); | ||||
| 
 | ||||
|     uint8_t   years =  rtc_time >> 40; | ||||
|     uint8_t  months = (rtc_time >> 32) & 0xff; | ||||
|     uint8_t    days = (rtc_time >> 24) & 0xff; | ||||
|     uint8_t   hours = (rtc_time >> 16) & 0xff; | ||||
|     uint8_t minutes = (rtc_time >>  8) & 0xff; | ||||
|     uint8_t seconds =  rtc_time        & 0xff; | ||||
| 
 | ||||
|     uint8_t month_table_index = | ||||
|       (years % 4) * 12 + months - 1; | ||||
| 
 | ||||
|     clamp(  years, 0, 99); | ||||
|     clamp( months, 1, 12); | ||||
|     clamp(   days, 1, month_table_2[month_table_index]); | ||||
|     clamp(  hours, 0, 23); | ||||
|     clamp(minutes, 0, 59); | ||||
|     clamp(seconds, 0, 59); | ||||
| 
 | ||||
|     current_time  = 1461 * (years / 4); | ||||
|     current_time += month_table[month_table_index]; | ||||
|     current_time += days - 1; | ||||
|     current_time *= 86400; | ||||
|     current_time += hours * 3600; | ||||
|     current_time += minutes * 60; | ||||
|     current_time += seconds; | ||||
|     current_time *= 1024; | ||||
| 
 | ||||
|     enable_rtc_interrupts(); | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   void register_sleeping_thread( | ||||
|     uint64_t sleeping_until, application::thread *thread) { | ||||
| 
 | ||||
|     auto *after = sleeping_threads->first; | ||||
|     while (after && after->value.sleeping_until < sleeping_until) | ||||
|       after = after->next; | ||||
| 
 | ||||
|     sleeping_threads->insert_before( | ||||
|       { .sleeping_until = sleeping_until, .thread = thread }, after); | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   extern "C" void acknowledge_rtc_interrupt(); | ||||
| 
 | ||||
|   extern "C" void on_rtc_interrupt() { | ||||
| 
 | ||||
|     ++current_time; | ||||
| 
 | ||||
|     while (sleeping_threads->first && | ||||
|            sleeping_threads->first->value.sleeping_until <= | ||||
|              current_time) { | ||||
|       application::paused_threads->insert( | ||||
|         sleeping_threads->first->value.thread); | ||||
|       sleeping_threads->remove(sleeping_threads->first); | ||||
|     } | ||||
| 
 | ||||
|     acknowledge_rtc_interrupt(); | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  | @ -5,18 +5,36 @@ | |||
| 
 | ||||
| namespace pake::widgets { | ||||
| 
 | ||||
|   //a widget that draws text with a daguerre::fixed_font<bool>
 | ||||
|   class fixed_text : public widget { | ||||
| 
 | ||||
|     //the font to use
 | ||||
|     const daguerre::fixed_font<bool> *font; | ||||
|     daguerre::hilbert_color bg, fg; | ||||
| 
 | ||||
|     //background color of the widget
 | ||||
|     daguerre::hilbert_color bg; | ||||
| 
 | ||||
|     //color of the text
 | ||||
|     daguerre::hilbert_color fg; | ||||
| 
 | ||||
|     //the text to draw
 | ||||
|     std::string text; | ||||
| 
 | ||||
|     //has the text changed since the last draw to render
 | ||||
|     bool text_changed; | ||||
| 
 | ||||
|     //the width and height of this widget, as set by notify_size
 | ||||
|     int width, height; | ||||
| 
 | ||||
|     //the alignment of the text within the region
 | ||||
|     halign ha; | ||||
|     valign va; | ||||
| 
 | ||||
|   public: | ||||
|     //TODO: look up font in some kind of catalogue
 | ||||
|     //text: the text to draw. this can be changed later by set_text.
 | ||||
|     //font: the font to use. TODO: pass a string and look up the font with that name
 | ||||
|     //bg: the background color of the widget. fg: the color of the text.
 | ||||
|     //ha, va: the alignment of the text within the widget
 | ||||
|     fixed_text( | ||||
|       std::string &&text, | ||||
|       const daguerre::fixed_font<bool> *font, | ||||
|  | @ -24,6 +42,9 @@ namespace pake::widgets { | |||
|       daguerre::hilbert_color fg, | ||||
|       halign ha, valign va); | ||||
| 
 | ||||
|     //change the text to draw
 | ||||
|     void set_text(std::string &&text); | ||||
| 
 | ||||
|     virtual void render( | ||||
|       dirtiable_image &onto, int x_off, int y_off, bool force) override; | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,25 +6,46 @@ | |||
| 
 | ||||
| namespace pake { | ||||
| 
 | ||||
|   //a window / a connection to the compositor.
 | ||||
|   class window { | ||||
| 
 | ||||
|     //the socket that connects us to the compositor
 | ||||
|     euler::syscall::stream_handle socket; | ||||
| 
 | ||||
|     //the size of the window
 | ||||
|     int width; | ||||
|     int height; | ||||
| 
 | ||||
|     //the rendered contents of the window. pixels are dirty when
 | ||||
|     //the compositor has not been informed of them changing.
 | ||||
|     dirtiable_image contents; | ||||
| 
 | ||||
|     //the root widget, or an unset pointer if there is no root widget set
 | ||||
|     std::unique_ptr<widget> root; | ||||
| 
 | ||||
|   public: | ||||
|     //create a new window / connection to the compositor
 | ||||
|     window(int width, int height, const std::string &title); | ||||
| 
 | ||||
|     //destroy the window / connection to the compositor
 | ||||
|     ~window(); | ||||
| 
 | ||||
|     //tell the compositor to show this window. you probably want to call
 | ||||
|     //set_root and render_and_send_to_compositor before calling this.
 | ||||
|     void show(); | ||||
| 
 | ||||
|     //tell the compositor to hide this window
 | ||||
|     void hide(); | ||||
| 
 | ||||
|     //set the root widget. the widget is notified that its size is the
 | ||||
|     //size of the window, and then it is rendered with force = true.
 | ||||
|     void set_root(std::unique_ptr<widget> &&w); | ||||
| 
 | ||||
|     //get the root widget (assumes there is one)
 | ||||
|     widget *get_root(); | ||||
| 
 | ||||
|     //renders the root widget with force = false and
 | ||||
|     //then sends the new contents to the compositor.
 | ||||
|     void render_and_send_to_compositor(); | ||||
| 
 | ||||
|   }; | ||||
|  |  | |||
|  | @ -15,12 +15,18 @@ namespace pake::widgets { | |||
|     daguerre::hilbert_color fg, | ||||
|     halign ha, valign va) | ||||
|   : font(font), bg(bg), fg(fg), | ||||
|     text(std::move(text)), ha(ha), va(va) {} | ||||
|     text(std::move(text)), | ||||
|     ha(ha), va(va) {} | ||||
| 
 | ||||
|   void fixed_text::set_text(std::string &&text) { | ||||
|     this->text = std::move(text); | ||||
|     text_changed = true; | ||||
|   } | ||||
| 
 | ||||
|   void fixed_text::render( | ||||
|     dirtiable_image &onto, int x_off, int y_off, bool force) { | ||||
| 
 | ||||
|     if (force) { | ||||
|     if (force || text_changed) { | ||||
| 
 | ||||
|       //TODO: check overflow
 | ||||
| 
 | ||||
|  | @ -54,6 +60,8 @@ namespace pake::widgets { | |||
|         *font, fg, x_off, y_off, | ||||
|         text.data(), draw_if_true); | ||||
| 
 | ||||
|       text_changed = false; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|   } | ||||
|  |  | |||
							
								
								
									
										9
									
								
								makefile
									
										
									
									
									
								
							
							
						
						
									
										9
									
								
								makefile
									
										
									
									
									
								
							|  | @ -44,6 +44,7 @@ clean: | |||
| 	make -C applications/init clean | ||||
| 	make -C applications/goldman clean | ||||
| 	make -C applications/hello clean | ||||
| 	make -C applications/clock clean | ||||
| 	make -C libraries/daguerre clean | ||||
| 	make -C libraries/pake clean | ||||
| 
 | ||||
|  | @ -136,15 +137,21 @@ applications/goldman/build/goldman.elf: ${APP_DEPS} ${DAGUERRE_DEP} | |||
| applications/hello/build/hello.elf: ${APP_DEPS} ${DAGUERRE_DEP} ${PAKE_DEP} | ||||
| 	+make -C applications/hello build/hello.elf | ||||
| 
 | ||||
| .PHONY: applications/clock/build/clock.elf | ||||
| applications/clock/build/clock.elf: ${APP_DEPS} ${DAGUERRE_DEP} ${PAKE_DEP} | ||||
| 	+make -C applications/clock build/clock.elf | ||||
| 
 | ||||
| build/initfs.tgz: applications/init/build/init.elf \ | ||||
|                   applications/goldman/build/goldman.elf \
 | ||||
|                   applications/hello/build/hello.elf | ||||
|                   applications/hello/build/hello.elf \
 | ||||
|                   applications/clock/build/clock.elf | ||||
| 	@mkdir -p build | ||||
| 	rm -rf build/initfs | ||||
| 	cp -r skeleton build/initfs | ||||
| 	cp applications/init/build/init.elf build/initfs/bin/init | ||||
| 	cp applications/goldman/build/goldman.elf build/initfs/bin/goldman | ||||
| 	cp applications/hello/build/hello.elf build/initfs/bin/hello | ||||
| 	cp applications/clock/build/clock.elf build/initfs/bin/clock | ||||
| 	cd build/initfs && tar czf ../initfs.tgz . | ||||
| 
 | ||||
| build/disk.iso: kernel/build/kernel.elf build/initfs.tgz ${LIMINE_DEP} | ||||
|  |  | |||
							
								
								
									
										10
									
								
								readme.txt
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								readme.txt
									
										
									
									
									
								
							|  | @ -67,12 +67,16 @@ the following directories and files are released under the text in cc0.txt | |||
| 
 | ||||
| project structure: | ||||
| 
 | ||||
|   - applications/clock: | ||||
|       a simple application that displays the current time in EDT. | ||||
| 
 | ||||
|   - applications/goldman: | ||||
|       the default compositor, in a very early stage | ||||
|       the default compositor. | ||||
| 
 | ||||
|   - applications/init: | ||||
|       the initial program loaded by the kernel. currently it just | ||||
|       (attempts to) start /bin/compositor and then /bin/hello. | ||||
|       the initial program loaded by the kernel. currently it just starts the | ||||
|       compositor and the clock, but in the future it will read some kind of | ||||
|       configuration file and decide what to do based on that. | ||||
| 
 | ||||
|   - documentation: | ||||
|       documentation. currently this directory is a bit disorganized, and has | ||||
|  |  | |||
		Reference in a new issue