From e60fa7740cd7d245d1b22a25fea9df0768d32668 Mon Sep 17 00:00:00 2001 From: Benji Dial Date: Sun, 19 May 2024 04:34:40 -0400 Subject: mouse support (working in qemu, semi-working in virtualbox) --- applications/init/source/main.cpp | 124 ++++++++++++++++--- documentation/kernel-interface/keys.txt | 78 ++++++++++++ documentation/kernel-interface/sockets.txt | 28 +++++ documentation/kernel-interface/syscalls.txt | 178 ++++++++++++++++++++++++++++ documentation/keys.txt | 78 ------------ documentation/sockets.txt | 28 ----- documentation/syscalls.txt | 168 -------------------------- euler/include/euler/syscall.hpp | 15 ++- euler/source/euler/syscall.asm | 28 ++++- kernel/include/hilbert/kernel/input.hpp | 21 +++- kernel/include/hilbert/kernel/utility.hpp | 5 + kernel/source/input.cpp | 4 +- kernel/source/interrupts.asm | 102 ++++++++++++++-- kernel/source/interrupts.cpp | 49 +++++++- kernel/source/syscall.cpp | 19 ++- libraries/daguerre/include/daguerre.hpp | 34 +++--- libraries/daguerre/source/daguerre.cpp | 15 ++- skeleton/assets/pointer.ppm | Bin 0 -> 1571 bytes skeleton/assets/readme.txt | 2 + 19 files changed, 637 insertions(+), 339 deletions(-) create mode 100644 documentation/kernel-interface/keys.txt create mode 100644 documentation/kernel-interface/sockets.txt create mode 100644 documentation/kernel-interface/syscalls.txt delete mode 100644 documentation/keys.txt delete mode 100644 documentation/sockets.txt delete mode 100644 documentation/syscalls.txt create mode 100644 skeleton/assets/pointer.ppm diff --git a/applications/init/source/main.cpp b/applications/init/source/main.cpp index ed0ef8e..6aa85a4 100644 --- a/applications/init/source/main.cpp +++ b/applications/init/source/main.cpp @@ -1,28 +1,120 @@ #include +void overlay_encode( + daguerre::hilbert_color &dest, const daguerre::rgb24 &src) { + if (src.r != 0xff || src.g != 0x00 || src.b != 0xff) + daguerre::encode(dest, src); +} + +void invert(daguerre::rgb24 &dest, const daguerre::rgb24 &src) { + dest.r = 255 - src.r; + dest.g = 255 - src.g; + dest.b = 255 - src.b; +} + int main(int, char **) { - auto hfb = daguerre::get_hilbert_framebuffer(); - daguerre::image bim; + auto framebuffer = daguerre::get_hilbert_framebuffer(); - std::FILE *burdon = std::fopen("/assets/burden.ppm", "r"); - daguerre::try_load_ppm(burdon, bim); - std::fclose(burdon); + daguerre::image burden; + std::FILE *burden_file = std::fopen("/assets/burden.ppm", "r"); + daguerre::try_load_ppm(burden_file, burden); + std::fclose(burden_file); - unsigned width = bim.width < hfb.width ? bim.width : hfb.width; - unsigned height = bim.height < hfb.height ? bim.height : hfb.height; - unsigned x = (hfb.width - width) / 2; - unsigned y = (hfb.height - height) / 2; + daguerre::image pointer; + std::FILE *pointer_file = std::fopen("/assets/pointer.ppm", "r"); + daguerre::try_load_ppm(pointer_file, pointer); + std::fclose(pointer_file); + + int32_t width = burden.width < framebuffer.width + ? burden.width : framebuffer.width; + int32_t height = burden.height < framebuffer.height + ? burden.height : framebuffer.height; + unsigned x = (framebuffer.width - width) / 2; + unsigned y = (framebuffer.height - height) / 2; + + int32_t new_mouse_x = width / 2; + int32_t new_mouse_y = height / 2; + int32_t old_mouse_x = new_mouse_x; + int32_t old_mouse_y = new_mouse_y; + bool was_left_mouse_down = false; + + daguerre::overlay_region< + daguerre::hilbert_color, daguerre::rgb24, daguerre::encode>( + framebuffer, x, y, burden, 0, 0, width, height); while (1) { - daguerre::copy_region<>(hfb, bim, 0, 0, x, y, width, height); - while ((__euler_read_key_packet() & 0x0400ff) != 0x00005a) - ; - for (unsigned i = 0; i < bim.width * bim.height; ++i) { - bim.buffer[i].r = 255 - bim.buffer[i].r; - bim.buffer[i].g = 255 - bim.buffer[i].g; - bim.buffer[i].b = 255 - bim.buffer[i].b; + + __euler_mouse_buttons mouse_buttons; + int16_t mouse_change_x, mouse_change_y; + uint32_t key_packet; + __euler_input_packet_type packet_type = __euler_get_input_packet( + mouse_buttons, mouse_change_x, mouse_change_y, key_packet); + + bool anything_changed = false; + bool should_invert = false; + + if (packet_type == __EULER_IPT_MOUSE) { + + if (mouse_change_x != 0 || mouse_change_y != 0) { + + old_mouse_x = new_mouse_x; + old_mouse_y = new_mouse_y; + new_mouse_x += mouse_change_x; + new_mouse_y += mouse_change_y; + + if (new_mouse_x < 0) + new_mouse_x = 0; + else if ((unsigned)new_mouse_x > width - pointer.width) + new_mouse_x = width - pointer.width; + + if (new_mouse_y < 0) + new_mouse_y = 0; + else if ((unsigned)new_mouse_y > height - pointer.height) + new_mouse_y = height - pointer.height; + + anything_changed = true; + + } + + if (!was_left_mouse_down && (mouse_buttons & __EULER_MB_LEFT)) + should_invert = true; + + was_left_mouse_down = mouse_buttons & __EULER_MB_LEFT; + + } + + else if (packet_type == __EULER_IPT_KEYBOARD) + if ((key_packet & 0x0400ff) == 0x00005a) + should_invert = true; + + if (should_invert) { + + //this works with the current implementation of overlay_region, but + //maybe it would be better to have a dedicated function for when the + //two regions are exactly the same, given the comment on overlay_region. + daguerre::overlay_region( + burden, 0, 0, burden, 0, 0, width, height); + + daguerre::overlay_region< + daguerre::hilbert_color, daguerre::rgb24, daguerre::encode>( + framebuffer, x, y, burden, 0, 0, width, height); + + anything_changed = true; + } + + if (anything_changed) { + daguerre::overlay_region< + daguerre::hilbert_color, daguerre::rgb24, daguerre::encode>( + framebuffer, old_mouse_x + x, old_mouse_y + y, burden, + old_mouse_x, old_mouse_y, pointer.width, pointer.height); + daguerre::overlay_region< + daguerre::hilbert_color, daguerre::rgb24, overlay_encode>( + framebuffer, new_mouse_x + x, new_mouse_y + y, + pointer, 0, 0, pointer.width, pointer.height); + } + } } diff --git a/documentation/kernel-interface/keys.txt b/documentation/kernel-interface/keys.txt new file mode 100644 index 0000000..1b92afa --- /dev/null +++ b/documentation/kernel-interface/keys.txt @@ -0,0 +1,78 @@ +a key packet, as returned by the kernel, is a 32-bit value. the top +13 bits are currently reserved. the next 11 bits are flags as follows: + 0x040000: break (as opposed to make) + 0x020000: num lock is on + 0x010000: caps lock is on + 0x008000: right windows + 0x004000: left windows + 0x002000: right alt + 0x001000: left alt + 0x000800: right ctrl + 0x000400: left ctrl + 0x000200: right shift + 0x000100: left shift +the bottom 8 bits represent the key. for us qwerty, they are: + 0x00: reserved | 0x40: reserved | 0x80: reserved | 0xc0: reserved + 0x01: f9 | 0x41: , / < | 0x81: reserved | 0xc1: reserved + 0x02: reserved | 0x42: k / K | 0x82: reserved | 0xc2: reserved + 0x03: f5 | 0x43: i / I | 0x83: reserved | 0xc3: reserved + 0x04: f3 | 0x44: o / O | 0x84: reserved | 0xc4: reserved + 0x05: f1 | 0x45: 0 / ) | 0x85: reserved | 0xc5: reserved + 0x06: f2 | 0x46: 9 / ( | 0x86: reserved | 0xc6: reserved + 0x07: f12 | 0x47: reserved | 0x87: f7 | 0xc7: reserved + 0x08: reserved | 0x48: reserved | 0x88: reserved | 0xc8: reserved + 0x09: f10 | 0x49: . / > | 0x89: reserved | 0xc9: reserved + 0x0a: f8 | 0x4a: / / ? | 0x8a: reserved | 0xca: numpad / + 0x0b: f6 | 0x4b: l / L | 0x8b: reserved | 0xcb: reserved + 0x0c: f4 | 0x4c: ; / : | 0x8c: reserved | 0xcc: reserved + 0x0d: tab | 0x4d: p / P | 0x8d: reserved | 0xcd: reserved + 0x0e: ` / ~ | 0x4e: - / _ | 0x8e: reserved | 0xce: reserved + 0x0f: reserved | 0x4f: reserved | 0x8f: reserved | 0xcf: reserved + 0x10: reserved | 0x50: reserved | 0x90: reserved | 0xd0: reserved + 0x11: left alt | 0x51: reserved | 0x91: right alt | 0xd1: reserved + 0x12: left shift | 0x52: ' / " | 0x92: | 0xd2: reserved + 0x13: reserved | 0x53: reserved | 0x93: reserved | 0xd3: reserved + 0x14: left ctrl | 0x54: [ / { | 0x94: right ctrl | 0xd4: reserved + 0x15: q / Q | 0x55: = / + | 0x95: reserved | 0xd5: reserved + 0x16: 1 / ! | 0x56: reserved | 0x96: reserved | 0xd6: reserved + 0x17: reserved | 0x57: reserved | 0x97: reserved | 0xd7: reserved + 0x18: reserved | 0x58: caps lock | 0x98: reserved | 0xd8: reserved + 0x19: reserved | 0x59: right shift | 0x99: reserved | 0xd9: reserved + 0x1a: z / Z | 0x5a: enter | 0x9a: reserved | 0xda: numpad enter + 0x1b: s / S | 0x5b: ] / } | 0x9b: reserved | 0xdb: reserved + 0x1c: a / A | 0x5c: reserved | 0x9c: reserved | 0xdc: reserved + 0x1d: w / W | 0x5d: \ / | | 0x9d: reserved | 0xdd: reserved + 0x1e: 2 / @ | 0x5e: reserved | 0x9e: reserved | 0xde: reserved + 0x1f: reserved | 0x5f: reserved | 0x9f: left win | 0xdf: reserved + 0x20: reserved | 0x60: reserved | 0xa0: reserved | 0xe0: print screen + 0x21: c / C | 0x61: reserved | 0xa1: reserved | 0xe1: pause + 0x22: x / X | 0x62: reserved | 0xa2: reserved | 0xe2: reserved + 0x23: d / D | 0x63: reserved | 0xa3: reserved | 0xe3: reserved + 0x24: e / E | 0x64: reserved | 0xa4: reserved | 0xe4: reserved + 0x25: 4 / $ | 0x65: reserved | 0xa5: reserved | 0xe5: reserved + 0x26: 3 / # | 0x66: backspace | 0xa6: reserved | 0xe6: reserved + 0x27: reserved | 0x67: reserved | 0xa7: right win | 0xe7: reserved + 0x28: reserved | 0x68: reserved | 0xa8: reserved | 0xe8: reserved + 0x29: space | 0x69: numpad 1 | 0xa9: reserved | 0xe9: end + 0x2a: v / V | 0x6a: reserved | 0xaa: reserved | 0xea: reserved + 0x2b: f / F | 0x6b: numpad 4 | 0xab: reserved | 0xeb: cursor left + 0x2c: t / T | 0x6c: numpad 7 | 0xac: reserved | 0xec: home + 0x2d: r / R | 0x6d: reserved | 0xad: reserved | 0xed: reserved + 0x2e: 5 / % | 0x6e: reserved | 0xae: reserved | 0xee: reserved + 0x2f: reserved | 0x6f: reserved | 0xaf: menu | 0xef: reserved + 0x30: reserved | 0x70: numpad 0 | 0xb0: reserved | 0xf0: insert + 0x31: n / N | 0x71: numpad . | 0xb1: reserved | 0xf1: delete + 0x32: b / B | 0x72: numpad 2 | 0xb2: reserved | 0xf2: cursor down + 0x33: h / H | 0x73: numpad 5 | 0xb3: reserved | 0xf3: reserved + 0x34: g / G | 0x74: numpad 6 | 0xb4: reserved | 0xf4: cursor right + 0x35: y / Y | 0x75: numpad 8 | 0xb5: reserved | 0xf5: cursor up + 0x36: 6 / ^ | 0x76: escape | 0xb6: reserved | 0xf6: reserved + 0x37: reserved | 0x77: num lock | 0xb7: reserved | 0xf7: reserved + 0x38: reserved | 0x78: f11 | 0xb8: reserved | 0xf8: reserved + 0x39: reserved | 0x79: numpad + | 0xb9: reserved | 0xf9: reserved + 0x3a: m / M | 0x7a: numpad 3 | 0xba: reserved | 0xfa: page down + 0x3b: j / J | 0x7b: numpad - | 0xbb: reserved | 0xfb: reserved + 0x3c: u / U | 0x7c: numpad * | 0xbc: reserved | 0xfc: reserved + 0x3d: 7 / & | 0x7d: numpad 9 | 0xbd: reserved | 0xfd: page up + 0x3e: 8 / * | 0x7e: scroll lock | 0xbe: reserved | 0xfe: reserved + 0x3f: reserved | 0x7f: reserved | 0xbf: reserved | 0xff: reserved diff --git a/documentation/kernel-interface/sockets.txt b/documentation/kernel-interface/sockets.txt new file mode 100644 index 0000000..73dade6 --- /dev/null +++ b/documentation/kernel-interface/sockets.txt @@ -0,0 +1,28 @@ +in hilbert os, a "socket" is a two-way byte-wise communication construct. each +socket has two ends, which can be either open or closed. each process has a +number of handles to sockets. sockets can be created in one of two ways: either +creating a private socket or connecting to a socket listener. + +private sockets: + a private socket is created with the "create private socket" system call. the + process creating the socket gets both ends of the socket. + +socket listeners: + a socket listener is created with the "create socket listener" system call. + an id string is passed to that system call and remains associated with the + listener throughout its lifetime. only one socket listener may have a given + id at once. while a socket listener exists, the owner of the listener can + call the "accept socket connection" system call, and any process can call the + "connect to socket" system call with that id passed. each of these system + calls blocks until the other occurs, at which point a socket is created with + the two process as its endpoints, and then both system calls return. the + listener remains alive after the socket is created, and can be used to create + more sockets until stopped with the "stop socket listener" system call. + +when a process is created, an end of a socket can be "gifted" to that process. +when that happens, the end remains open, and is now accessible by the giftee +and not by the gifter. + +when either end of a socket is closed, the other end of the socket remains +valid, and can be read from until empty. when both ends of a socket are closed, +the socket disappears. diff --git a/documentation/kernel-interface/syscalls.txt b/documentation/kernel-interface/syscalls.txt new file mode 100644 index 0000000..792c300 --- /dev/null +++ b/documentation/kernel-interface/syscalls.txt @@ -0,0 +1,178 @@ +on application entry: + there is a 1MiB - 8KiB area mapped writable and not + executable with guard pages on either side, and rsp is + set to the top of that. all other registers are set to 0. + +the ARGC environment variable holds the number of arguments to main. +the ARGV0, ARGV1, ARGV2, etc environment variables hold those arguments. + +for all system calls: + rax, rdi, rsi, rdx are in/out paramters. + rbx, rbp, rsp, r12-r15 are preserved. + rcx, rflags, r8-r11 are clobbered. + +interrupts (including the timer!) are disabled during system calls. + +stream result: + 0 = success + 1 = bad handle + 2 = io error + 3 = out of bounds + 4 = does not exist + 5 = not a regular file + 6 = not an executable + 7 = not writable + 8 = not seekable + 9 = socket id already used + 10 = socket id not in use + 11 = socket listener closed + 12 = other end closed + 13 = already exists + 14 = not sized + +encode color: + rax in: 0 + edi in: r + g * 256 + b * 65536 + eax out: encoded color + +get framebuffer: + rax in: 1 + rax out: pointer to framebuffer + rdi out: width + height * 2 ** 32 + esi out: pitch + framebuffer is always 32 bpp. use the encode color syscall + to encode colors. pitch is in dwords, not in bytes. + +open file: + rax in: 2 + rdi in: pointer to file path + rsi in: file path length + rdx in: + bit 0: allow creation + bit 1: only allow creation + rax out: stream result + rdi out: stream handle (if rax = success) + +end this thread: + rax in: 3 + edi in: exit code (signed, only used if this was last thread) + +get new pages: + rax in: 4 + rdi in: number of pages to allocate + rax out: start of first page + the allocated pages are next to each other, writable, and not executable. + +get input packet: + rax in: 5 + al out: + bit 7: was mouse packet + if bit 7: + bit 0: left mouse button down + bit 1: right mouse button down + bit 2: middle mouse button down + if bit 7: + di out: mouse x change (signed) + si out: mouse y change (signed) + else: + edi out: key packet + +create private socket: + rax in: 6 + rax out: end 1 stream handle + rdi out: end 2 stream handle + +create socket listener: + rax in: 7 + rdi in: pointer to id string + rsi in: id string length + rax out: stream result + rdi out: listener handle (if rax = 0) + +stop socket listener: + rax in: 8 + rdi in: listener handle + +accept socket connection: + rax in: 9 + rdi in: listener handle + rax out: stream result + rdi out: stream handle + +connect to socket: + rax in: 10 + rdi in: pointer to id string + rsi in: id string length + rax out: stream result + rdi out: stream handle (if rax = 0) + if the listener is closed while this syscall is blocked, rax is + set to "socket id not in use" and not "socket listener closed" + +close stream: + rax in: 11 + rdi in: stream handle + +seek stream: + returns "not seekable" for sockets + rax in: 12 + rdi in: stream handle + sil in: + 0 = relative to beginning + 1 = relative to end + 2 = relative to current position + rdx in: offset (signed) + rax out: stream result + +read from stream: + rax in: 13 + rdi in: stream handle + rsi in: count + rdx in: pointer to buffer + rax out: stream result + +write to stream: + rax in: 14 + rdi in: stream handle + rsi in: count + rdx in: pointer to buffer + rax out: stream result + +get stream length: + returns "not sized" for sockets + rax in: 15 + rdi in: stream handle + rax out: stream result + rdi out: stream length (if rax = success) + +start process: + rax in: 16 + rdi in: pointer to process start info struct + rax out: stream result + rdi out: process handle (if rax = success) + process start info struct: + qword: file path length + qword: pointer to file path + qword: count of environment variables + qword: pointer to array of environment variable structs + qword: count of gifted stream ends + qword: pointer to array of gifted stream structs + environment variable struct: + qword: name length + qword: pointer to name + qword: value length + qword: pointer to value + gifted stream struct: + qword: stream handle here + qword: new stream handle in child + new handle must be < 65536 + +end this process: + rax in: 17 + edi in: exit code (signed) + +set stream length: + returns "not sized" for sockets + rax in: 18 + rdi in: stream handle + rsi in: new length + rax out: stream result diff --git a/documentation/keys.txt b/documentation/keys.txt deleted file mode 100644 index 1b92afa..0000000 --- a/documentation/keys.txt +++ /dev/null @@ -1,78 +0,0 @@ -a key packet, as returned by the kernel, is a 32-bit value. the top -13 bits are currently reserved. the next 11 bits are flags as follows: - 0x040000: break (as opposed to make) - 0x020000: num lock is on - 0x010000: caps lock is on - 0x008000: right windows - 0x004000: left windows - 0x002000: right alt - 0x001000: left alt - 0x000800: right ctrl - 0x000400: left ctrl - 0x000200: right shift - 0x000100: left shift -the bottom 8 bits represent the key. for us qwerty, they are: - 0x00: reserved | 0x40: reserved | 0x80: reserved | 0xc0: reserved - 0x01: f9 | 0x41: , / < | 0x81: reserved | 0xc1: reserved - 0x02: reserved | 0x42: k / K | 0x82: reserved | 0xc2: reserved - 0x03: f5 | 0x43: i / I | 0x83: reserved | 0xc3: reserved - 0x04: f3 | 0x44: o / O | 0x84: reserved | 0xc4: reserved - 0x05: f1 | 0x45: 0 / ) | 0x85: reserved | 0xc5: reserved - 0x06: f2 | 0x46: 9 / ( | 0x86: reserved | 0xc6: reserved - 0x07: f12 | 0x47: reserved | 0x87: f7 | 0xc7: reserved - 0x08: reserved | 0x48: reserved | 0x88: reserved | 0xc8: reserved - 0x09: f10 | 0x49: . / > | 0x89: reserved | 0xc9: reserved - 0x0a: f8 | 0x4a: / / ? | 0x8a: reserved | 0xca: numpad / - 0x0b: f6 | 0x4b: l / L | 0x8b: reserved | 0xcb: reserved - 0x0c: f4 | 0x4c: ; / : | 0x8c: reserved | 0xcc: reserved - 0x0d: tab | 0x4d: p / P | 0x8d: reserved | 0xcd: reserved - 0x0e: ` / ~ | 0x4e: - / _ | 0x8e: reserved | 0xce: reserved - 0x0f: reserved | 0x4f: reserved | 0x8f: reserved | 0xcf: reserved - 0x10: reserved | 0x50: reserved | 0x90: reserved | 0xd0: reserved - 0x11: left alt | 0x51: reserved | 0x91: right alt | 0xd1: reserved - 0x12: left shift | 0x52: ' / " | 0x92: | 0xd2: reserved - 0x13: reserved | 0x53: reserved | 0x93: reserved | 0xd3: reserved - 0x14: left ctrl | 0x54: [ / { | 0x94: right ctrl | 0xd4: reserved - 0x15: q / Q | 0x55: = / + | 0x95: reserved | 0xd5: reserved - 0x16: 1 / ! | 0x56: reserved | 0x96: reserved | 0xd6: reserved - 0x17: reserved | 0x57: reserved | 0x97: reserved | 0xd7: reserved - 0x18: reserved | 0x58: caps lock | 0x98: reserved | 0xd8: reserved - 0x19: reserved | 0x59: right shift | 0x99: reserved | 0xd9: reserved - 0x1a: z / Z | 0x5a: enter | 0x9a: reserved | 0xda: numpad enter - 0x1b: s / S | 0x5b: ] / } | 0x9b: reserved | 0xdb: reserved - 0x1c: a / A | 0x5c: reserved | 0x9c: reserved | 0xdc: reserved - 0x1d: w / W | 0x5d: \ / | | 0x9d: reserved | 0xdd: reserved - 0x1e: 2 / @ | 0x5e: reserved | 0x9e: reserved | 0xde: reserved - 0x1f: reserved | 0x5f: reserved | 0x9f: left win | 0xdf: reserved - 0x20: reserved | 0x60: reserved | 0xa0: reserved | 0xe0: print screen - 0x21: c / C | 0x61: reserved | 0xa1: reserved | 0xe1: pause - 0x22: x / X | 0x62: reserved | 0xa2: reserved | 0xe2: reserved - 0x23: d / D | 0x63: reserved | 0xa3: reserved | 0xe3: reserved - 0x24: e / E | 0x64: reserved | 0xa4: reserved | 0xe4: reserved - 0x25: 4 / $ | 0x65: reserved | 0xa5: reserved | 0xe5: reserved - 0x26: 3 / # | 0x66: backspace | 0xa6: reserved | 0xe6: reserved - 0x27: reserved | 0x67: reserved | 0xa7: right win | 0xe7: reserved - 0x28: reserved | 0x68: reserved | 0xa8: reserved | 0xe8: reserved - 0x29: space | 0x69: numpad 1 | 0xa9: reserved | 0xe9: end - 0x2a: v / V | 0x6a: reserved | 0xaa: reserved | 0xea: reserved - 0x2b: f / F | 0x6b: numpad 4 | 0xab: reserved | 0xeb: cursor left - 0x2c: t / T | 0x6c: numpad 7 | 0xac: reserved | 0xec: home - 0x2d: r / R | 0x6d: reserved | 0xad: reserved | 0xed: reserved - 0x2e: 5 / % | 0x6e: reserved | 0xae: reserved | 0xee: reserved - 0x2f: reserved | 0x6f: reserved | 0xaf: menu | 0xef: reserved - 0x30: reserved | 0x70: numpad 0 | 0xb0: reserved | 0xf0: insert - 0x31: n / N | 0x71: numpad . | 0xb1: reserved | 0xf1: delete - 0x32: b / B | 0x72: numpad 2 | 0xb2: reserved | 0xf2: cursor down - 0x33: h / H | 0x73: numpad 5 | 0xb3: reserved | 0xf3: reserved - 0x34: g / G | 0x74: numpad 6 | 0xb4: reserved | 0xf4: cursor right - 0x35: y / Y | 0x75: numpad 8 | 0xb5: reserved | 0xf5: cursor up - 0x36: 6 / ^ | 0x76: escape | 0xb6: reserved | 0xf6: reserved - 0x37: reserved | 0x77: num lock | 0xb7: reserved | 0xf7: reserved - 0x38: reserved | 0x78: f11 | 0xb8: reserved | 0xf8: reserved - 0x39: reserved | 0x79: numpad + | 0xb9: reserved | 0xf9: reserved - 0x3a: m / M | 0x7a: numpad 3 | 0xba: reserved | 0xfa: page down - 0x3b: j / J | 0x7b: numpad - | 0xbb: reserved | 0xfb: reserved - 0x3c: u / U | 0x7c: numpad * | 0xbc: reserved | 0xfc: reserved - 0x3d: 7 / & | 0x7d: numpad 9 | 0xbd: reserved | 0xfd: page up - 0x3e: 8 / * | 0x7e: scroll lock | 0xbe: reserved | 0xfe: reserved - 0x3f: reserved | 0x7f: reserved | 0xbf: reserved | 0xff: reserved diff --git a/documentation/sockets.txt b/documentation/sockets.txt deleted file mode 100644 index 73dade6..0000000 --- a/documentation/sockets.txt +++ /dev/null @@ -1,28 +0,0 @@ -in hilbert os, a "socket" is a two-way byte-wise communication construct. each -socket has two ends, which can be either open or closed. each process has a -number of handles to sockets. sockets can be created in one of two ways: either -creating a private socket or connecting to a socket listener. - -private sockets: - a private socket is created with the "create private socket" system call. the - process creating the socket gets both ends of the socket. - -socket listeners: - a socket listener is created with the "create socket listener" system call. - an id string is passed to that system call and remains associated with the - listener throughout its lifetime. only one socket listener may have a given - id at once. while a socket listener exists, the owner of the listener can - call the "accept socket connection" system call, and any process can call the - "connect to socket" system call with that id passed. each of these system - calls blocks until the other occurs, at which point a socket is created with - the two process as its endpoints, and then both system calls return. the - listener remains alive after the socket is created, and can be used to create - more sockets until stopped with the "stop socket listener" system call. - -when a process is created, an end of a socket can be "gifted" to that process. -when that happens, the end remains open, and is now accessible by the giftee -and not by the gifter. - -when either end of a socket is closed, the other end of the socket remains -valid, and can be read from until empty. when both ends of a socket are closed, -the socket disappears. diff --git a/documentation/syscalls.txt b/documentation/syscalls.txt deleted file mode 100644 index 52e909d..0000000 --- a/documentation/syscalls.txt +++ /dev/null @@ -1,168 +0,0 @@ -on application entry: - there is a 1MiB - 8KiB area mapped writable and not - executable with guard pages on either side, and rsp is - set to the top of that. all other registers are set to 0. - -the ARGC environment variable holds the number of arguments to main. -the ARGV0, ARGV1, ARGV2, etc environment variables hold those arguments. - -for all system calls: - rax, rdi, rsi, rdx are in/out paramters. - rbx, rbp, rsp, r12-r15 are preserved. - rcx, rflags, r8-r11 are clobbered. - -interrupts (including the timer!) are disabled during system calls. - -stream result: - 0 = success - 1 = bad handle - 2 = io error - 3 = out of bounds - 4 = does not exist - 5 = not a regular file - 6 = not an executable - 7 = not writable - 8 = not seekable - 9 = socket id already used - 10 = socket id not in use - 11 = socket listener closed - 12 = other end closed - 13 = already exists - 14 = not sized - -encode color: - rax in: 0 - edi in: r + g * 256 + b * 65536 - eax out: encoded color - -get framebuffer: - rax in: 1 - rax out: pointer to framebuffer - rdi out: width + height * 2 ** 32 - esi out: pitch - framebuffer is always 32 bpp. use the encode color syscall - to encode colors. pitch is in dwords, not in bytes. - -open file: - rax in: 2 - rdi in: pointer to file path - rsi in: file path length - rdx in: - bit 0: allow creation - bit 1: only allow creation - rax out: stream result - rdi out: stream handle (if rax = success) - -end this thread: - rax in: 3 - edi in: exit code (signed, only used if this was last thread) - -get new pages: - rax in: 4 - rdi in: number of pages to allocate - rax out: start of first page - the allocated pages are next to each other, writable, and not executable. - -read key packet: - rax in: 5 - eax out: key packet - -create private socket: - rax in: 6 - rax out: end 1 stream handle - rdi out: end 2 stream handle - -create socket listener: - rax in: 7 - rdi in: pointer to id string - rsi in: id string length - rax out: stream result - rdi out: listener handle (if rax = 0) - -stop socket listener: - rax in: 8 - rdi in: listener handle - -accept socket connection: - rax in: 9 - rdi in: listener handle - rax out: stream result - rdi out: stream handle - -connect to socket: - rax in: 10 - rdi in: pointer to id string - rsi in: id string length - rax out: stream result - rdi out: stream handle (if rax = 0) - if the listener is closed while this syscall is blocked, rax is - set to "socket id not in use" and not "socket listener closed" - -close stream: - rax in: 11 - rdi in: stream handle - -seek stream: - returns "not seekable" for sockets - rax in: 12 - rdi in: stream handle - sil in: - 0 = relative to beginning - 1 = relative to end - 2 = relative to current position - rdx in: offset (signed) - rax out: stream result - -read from stream: - rax in: 13 - rdi in: stream handle - rsi in: count - rdx in: pointer to buffer - rax out: stream result - -write to stream: - rax in: 14 - rdi in: stream handle - rsi in: count - rdx in: pointer to buffer - rax out: stream result - -get stream length: - returns "not sized" for sockets - rax in: 15 - rdi in: stream handle - rax out: stream result - rdi out: stream length (if rax = success) - -start process: - rax in: 16 - rdi in: pointer to process start info struct - rax out: stream result - rdi out: process handle (if rax = success) - process start info struct: - qword: file path length - qword: pointer to file path - qword: count of environment variables - qword: pointer to array of environment variable structs - qword: count of gifted stream ends - qword: pointer to array of gifted stream structs - environment variable struct: - qword: name length - qword: pointer to name - qword: value length - qword: pointer to value - gifted stream struct: - qword: stream handle here - qword: new stream handle in child - new handle must be < 65536 - -end this process: - rax in: 17 - edi in: exit code (signed) - -set stream length: - returns "not sized" for sockets - rax in: 18 - rdi in: stream handle - rsi in: new length - rax out: stream result diff --git a/euler/include/euler/syscall.hpp b/euler/include/euler/syscall.hpp index 9255642..761dbcc 100644 --- a/euler/include/euler/syscall.hpp +++ b/euler/include/euler/syscall.hpp @@ -58,4 +58,17 @@ extern "C" uint32_t *__euler_get_framebuffer( extern "C" uint32_t __euler_encode_color(uint8_t r, uint8_t g, uint8_t b); -extern "C" uint32_t __euler_read_key_packet(); +enum __euler_mouse_buttons : uint8_t { + __EULER_MB_LEFT = 1, + __EULER_MB_RIGHT = 2, + __EULER_MB_MIDDLE = 4 +}; + +enum __euler_input_packet_type : uint8_t { + __EULER_IPT_MOUSE = 1, + __EULER_IPT_KEYBOARD = 2 +}; + +extern "C" __euler_input_packet_type __euler_get_input_packet( + __euler_mouse_buttons &buttons_out, int16_t &x_change_out, + int16_t &y_change_out, uint32_t &keyboard_packet_out); diff --git a/euler/source/euler/syscall.asm b/euler/source/euler/syscall.asm index 34f2735..41bd05c 100644 --- a/euler/source/euler/syscall.asm +++ b/euler/source/euler/syscall.asm @@ -11,7 +11,7 @@ global __euler_end_this_thread global __euler_read_from_stream global __euler_get_framebuffer global __euler_encode_color -global __euler_read_key_packet +global __euler_get_input_packet section .text @@ -96,7 +96,31 @@ __euler_encode_color: syscall ret -__euler_read_key_packet: +__euler_get_input_packet: + push rdi + push rsi + push rdx + push rcx mov rax, 5 syscall + + test al, 0x80 + jnz .mouse_packet + + pop rcx + mov dword [rcx], edi + add rsp, 24 + mov al, 2 + ret + +.mouse_packet: + add rsp, 8 + pop rdx + mov word [rdx], si + pop rdx + mov word [rdx], di + pop rdx + and al, 0x7f + mov byte [rdx], al + mov al, 1 ret diff --git a/kernel/include/hilbert/kernel/input.hpp b/kernel/include/hilbert/kernel/input.hpp index 2209ddc..d1b7ca2 100644 --- a/kernel/include/hilbert/kernel/input.hpp +++ b/kernel/include/hilbert/kernel/input.hpp @@ -18,7 +18,26 @@ namespace hilbert::kernel::input { BREAK = 1 << 18, }; - extern utility::queue *key_queue; + enum buttons_t : uint8_t { + LEFT_BUTTON = 1, + RIGHT_BUTTON = 2, + MIDDLE_BUTTON = 4 + }; + + struct input_packet { + union { + struct { + int16_t x_change; + int16_t y_change; + buttons_t buttons; + } mouse; + uint32_t keyboard; + }; + bool is_mouse; + }; + + extern utility::queue *input_queue; + //notify a process waiting for input void got_input(); diff --git a/kernel/include/hilbert/kernel/utility.hpp b/kernel/include/hilbert/kernel/utility.hpp index 47f78ea..c0b8c19 100644 --- a/kernel/include/hilbert/kernel/utility.hpp +++ b/kernel/include/hilbert/kernel/utility.hpp @@ -342,6 +342,11 @@ namespace hilbert::kernel::utility { return move(ret); } + //assumes not empty + value_t &last_inserted() const { + return buffer[(count - 1 + next_read_index) % buffer_len]; + } + }; } diff --git a/kernel/source/input.cpp b/kernel/source/input.cpp index 696cb13..921ae7b 100644 --- a/kernel/source/input.cpp +++ b/kernel/source/input.cpp @@ -5,10 +5,10 @@ namespace hilbert::kernel::input { - utility::queue *key_queue; + utility::queue *input_queue; void init_input() { - key_queue = new utility::queue(); + input_queue = new utility::queue(); } void got_input() { diff --git a/kernel/source/interrupts.asm b/kernel/source/interrupts.asm index babc020..e0c3cdb 100644 --- a/kernel/source/interrupts.asm +++ b/kernel/source/interrupts.asm @@ -227,14 +227,6 @@ section .bss section .text -write_keyboard_byte: - in al, 0x64 - test al, 0x02 - jnz write_keyboard_byte - mov al, dil - out 0x60, al - ret - extern on_keyboard_interrupt keyboard_isr: @@ -249,6 +241,7 @@ keyboard_isr: push rcx push rax + call wait_read_ps2 in al, 0x60 mov dil, al @@ -269,6 +262,54 @@ keyboard_isr: iretq +extern on_mouse_interrupt + +mouse_isr: + + push r11 + push r10 + push r9 + push r8 + push rsi + push rdi + push rdx + push rcx + push rax + + call wait_read_ps2 + in al, 0x60 + mov dil, al + + call on_mouse_interrupt + + mov al, 0x20 + out 0x20, al + out 0xa0, al + + pop rax + pop rcx + pop rdx + pop rdi + pop rsi + pop r8 + pop r9 + pop r10 + pop r11 + + iretq + +wait_send_ps2: + in al, 0x64 + test al, 0x02 + jnz wait_send_ps2 + ret + +wait_read_ps2: + in al, 0x64 + test al, 0x01 + jz wait_send_ps2 + ret + load_gdt_and_idt: ;fill exception entries in idt @@ -294,7 +335,7 @@ load_gdt_and_idt: out 0x21, al mov al, 0x01 out 0x21, al - mov al, 0xfd ;mask all but irq 1 + mov al, 0xf9 ;mask all but irq 1 and 2 out 0x21, al mov al, 0x11 @@ -305,19 +346,54 @@ load_gdt_and_idt: out 0xa1, al mov al, 0x01 out 0xa1, al - mov al, 0xff ;mask all + mov al, 0xef ;mask all but irq 12 out 0xa1, al + ;register keyboard and mouse interrupts + mov rdi, 0x21 mov rsi, keyboard_isr call set_isr - ;set keyboard config + mov rdi, 0x2c + mov rsi, mouse_isr + call set_isr + + ;set ps2 config + call wait_send_ps2 mov al, 0x60 out 0x64, al - mov dil, 0x01 - call write_keyboard_byte + + call wait_send_ps2 + mov al, 0x03 + out 0x60, al + + ;set mouse defaults + + call wait_send_ps2 + mov al, 0xd4 + out 0x64, al + + call wait_send_ps2 + mov al, 0xf6 + out 0x60, al + + call wait_read_ps2 + in al, 0x60 + + ;enable mouse reporting + + call wait_send_ps2 + mov al, 0xd4 + out 0x64, al + + call wait_send_ps2 + mov al, 0xf4 + out 0x60, al + + call wait_read_ps2 + in al, 0x60 ;make tss entry in gdt diff --git a/kernel/source/interrupts.cpp b/kernel/source/interrupts.cpp index 6e22121..9b6495e 100644 --- a/kernel/source/interrupts.cpp +++ b/kernel/source/interrupts.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -54,7 +55,9 @@ static uint32_t current_flags = 0; static void got_key(uint32_t key) { - input::key_queue->insert(current_flags | key); + input::input_queue->insert({ + .keyboard = current_flags | key, .is_mouse = false}); + input::got_input(); if (key == (input::BREAK | 0x77)) @@ -90,7 +93,7 @@ static void got_key(uint32_t key) { } static uint8_t key_so_far[8]; -uint8_t key_so_far_len = 0; +static uint8_t key_so_far_len = 0; extern "C" void on_keyboard_interrupt(uint8_t byte) { @@ -150,3 +153,45 @@ extern "C" void on_keyboard_interrupt(uint8_t byte) { } } + +static uint8_t mouse_packet_so_far[3]; +static uint8_t mouse_packet_so_far_len = 0; + +extern "C" void on_mouse_interrupt(uint8_t byte) { + + if (mouse_packet_so_far_len == 0 && byte == 0xfa) + //dirty hack + return; + + mouse_packet_so_far[mouse_packet_so_far_len++] = byte; + if (mouse_packet_so_far_len < 3) + return; + + mouse_packet_so_far_len = 0; + + int16_t x_change = mouse_packet_so_far[1]; + int16_t y_change = -(int16_t)mouse_packet_so_far[2]; + if (mouse_packet_so_far[0] & 0x10) + x_change -= 0x100; + if (mouse_packet_so_far[0] & 0x20) + y_change += 0x100; + + input::input_packet packet = { + .mouse = { + .x_change = x_change, .y_change = y_change, + .buttons = (input::buttons_t)(mouse_packet_so_far[0] & 7) }, + .is_mouse = true }; + + if (input::input_queue->count > 0 && + input::input_queue->last_inserted().is_mouse && + input::input_queue->last_inserted().mouse.buttons == + packet.mouse.buttons) { + input::input_queue->last_inserted().mouse.x_change = packet.mouse.x_change; + input::input_queue->last_inserted().mouse.y_change = packet.mouse.y_change; + } + else + input::input_queue->insert(packet); + + input::got_input(); + +} diff --git a/kernel/source/syscall.cpp b/kernel/source/syscall.cpp index 768ff0d..5d9714c 100644 --- a/kernel/source/syscall.cpp +++ b/kernel/source/syscall.cpp @@ -157,7 +157,7 @@ namespace hilbert::kernel::syscall { } - void read_key_packet_syscall( + void get_input_packet_syscall( uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx ) { @@ -165,8 +165,17 @@ namespace hilbert::kernel::syscall { auto *t = application::running_thread; do - if (input::key_queue->count > 0) { - rax = (uint64_t)input::key_queue->take(); + if (input::input_queue->count > 0) { + input::input_packet packet = input::input_queue->take(); + if (packet.is_mouse) { + rax = packet.mouse.buttons | 0x80; + rdi = (uint16_t)packet.mouse.x_change; + rsi = (uint16_t)packet.mouse.y_change; + } + else { + rax = 0; + rdi = packet.keyboard; + } return; } while (application::save_thread_state(t->cpu)); @@ -504,7 +513,7 @@ namespace hilbert::kernel::syscall { &open_file_syscall, &end_this_thread_syscall, &get_new_pages_syscall, - &read_key_packet_syscall, + &get_input_packet_syscall, &create_private_socket_syscall, &create_socket_listener_syscall, &stop_socket_listener_syscall, @@ -520,7 +529,7 @@ namespace hilbert::kernel::syscall { &set_stream_length_syscall }; - static constexpr int max_syscall_number = 18; + static constexpr int max_syscall_number = 19; } diff --git a/libraries/daguerre/include/daguerre.hpp b/libraries/daguerre/include/daguerre.hpp index 274e257..62d10f0 100644 --- a/libraries/daguerre/include/daguerre.hpp +++ b/libraries/daguerre/include/daguerre.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include namespace daguerre { @@ -13,14 +14,6 @@ namespace daguerre { uint8_t b; }; - template - to_type convert_color(const from_type &from); - - template <> - inline hilbert_color convert_color(const rgb24 &from) { - return __euler_encode_color(from.r, from.g, from.b); - } - template class image { @@ -88,15 +81,15 @@ namespace daguerre { //from [from_x, from_x + width) x [from_y, from_y + height). template void copy_region( - image &to, const image &from, unsigned from_x, - unsigned from_y, unsigned to_x, unsigned to_y, unsigned width, - unsigned height) { + image &to, unsigned to_x, unsigned to_y, + const image &from, unsigned from_x, unsigned from_y, + unsigned width, unsigned height) { color_t *to_start = to.buffer + to.pitch * to_y + to_x; const color_t *from_start = from.buffer + from.pitch * from_y + from_x; for (unsigned y = 0; y < height; ++y) - memcpy( + std::memcpy( to_start + to.pitch * y, from_start + from.pitch * y, width * sizeof(color_t)); @@ -107,12 +100,11 @@ namespace daguerre { //from [from_x, from_x + width) x [from_y, from_y + height). template < class to_color_t, class from_color_t, - to_color_t converter(const from_color_t &) = - convert_color> - void copy_region( - image &to, const image &from, unsigned from_x, - unsigned from_y, unsigned to_x, unsigned to_y, unsigned width, - unsigned height) { + void overlay(to_color_t &dest, const from_color_t &src)> + void overlay_region( + image &to, unsigned to_x, unsigned to_y, + const image &from, unsigned from_x, unsigned from_y, + unsigned width, unsigned height) { to_color_t *to_start = to.buffer + to.pitch * to_y + to_x; const from_color_t *from_start = @@ -120,8 +112,12 @@ namespace daguerre { for (unsigned y = 0; y < height; ++y) for (unsigned x = 0; x < width; ++x) - to_start[to.pitch * y + x] = converter(from_start[from.pitch * y + x]); + overlay(to_start[to.pitch * y + x], from_start[from.pitch * y + x]); + + } + static inline void encode(hilbert_color &dest, const rgb24 &src) { + dest = __euler_encode_color(src.r, src.g, src.b); } image get_hilbert_framebuffer(); diff --git a/libraries/daguerre/source/daguerre.cpp b/libraries/daguerre/source/daguerre.cpp index fb3ddc7..7c13a88 100644 --- a/libraries/daguerre/source/daguerre.cpp +++ b/libraries/daguerre/source/daguerre.cpp @@ -8,15 +8,22 @@ namespace daguerre { return image(ptr, width, height, pitch, false); } + //TODO: make this more robust unsigned read_text_int(std::FILE *input) { unsigned n = 0; char ch; - while (true) { + std::fread(&ch, 1, 1, input); + if (ch == '#') { + do + std::fread(&ch, 1, 1, input); + while (ch != '\n'); std::fread(&ch, 1, 1, input); - if (ch < '0' || ch > '9') - return n; - n = n * 10 + ch - '0'; } + do { + n = n * 10 + ch - '0'; + std::fread(&ch, 1, 1, input); + } while (ch >= '0' && ch <= '9'); + return n; } bool try_load_ppm(std::FILE *input, image &into) { diff --git a/skeleton/assets/pointer.ppm b/skeleton/assets/pointer.ppm new file mode 100644 index 0000000..7169033 Binary files /dev/null and b/skeleton/assets/pointer.ppm differ diff --git a/skeleton/assets/readme.txt b/skeleton/assets/readme.txt index 56b1a9b..72476c5 100644 --- a/skeleton/assets/readme.txt +++ b/skeleton/assets/readme.txt @@ -2,3 +2,5 @@ the photo in burden.ppm is "selective focus photography snowflakes" by aaron burden. it can be found online at https://unsplash.com/photos/selective-focus-photography-snowflakes-9yhy1FXlKwI. its license can be found online at https://unsplash.com/license. + +the icon in pointer.ppm is by me :) -- cgit v1.2.3