diff --git a/CMakeLists.txt b/CMakeLists.txt index 02d6daa..130440e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,8 +14,10 @@ add_executable(1_test src/l1/tests/t1.c) add_executable(0_render_test src/l2/tests/r0.c) target_link_libraries(0_render_test -lvulkan -lX11 -lm) +add_executable(0_play_test src/l3/tests/p0.c) +target_link_libraries(0_play_test -lncurses) + # Recursively collect all .h files in the src directory. file(GLOB_RECURSE HEADER_FILES "${CMAKE_SOURCE_DIR}/src/*.h") - # Do not build utku add_executable(utka src/l1/main.c ${HEADER_FILES}) diff --git a/earth_doc/eng_FunMachine_description.typ b/earth_doc/eng_FunMachine_description.typ new file mode 100644 index 0000000..bc18564 --- /dev/null +++ b/earth_doc/eng_FunMachine_description.typ @@ -0,0 +1,56 @@ +#align(center)[= JSM-3 (Fun Machine) ] + +*This is not an official documentation. To get an official +technical documentation of JSM-3 you need to contact Dakah +representatives. * This is an unofficial review, +that includes both detailed detailed guide on writing in JSM-3 machine +code and a historical background on Fun Machine. + +This is the 15th issue of Scratchtip journal. + +*JSM-3* (or simply *Fun Machine*) is a computer manufactured by Dakah#sym.trademark +corporation. It was first manufactured in a run of 100 items +on $#[50]#sym.star.op#[233]$. It was designed for digital entertainment. +Failed immediately, because nobody really needed a gaming +computer with 32x32 resolution. Up to that point it is only remembered because +it was the first publically available model from JSM series. + +== Review of external qualities + +Fun Machine a nefarious 32x32 led display on a 30x30 cm panel, wielded to the +main body of the computer. It has 1 set of 16 levers on it and a +power button with a led power indicator. Keyboard is not wielded to the main +corpus. It is connected to it by a 1 meter cable. Keyboard contains +50 same size keys. Keyboard is 40x25 cm in size. JSM-3 features 2^17 bytes (2^16 +words) of RAM and a JSM-3-Core CPU. JSM-3-Core is capable of running at +80'000 tps. It should be noted, that operations that access high memory +addresses can take seveal ticks to execute. The "bootstick reader" is located on the +back of Fun Machine's frame. It is a long slide, that can read bootsticks t boot. +Bootstick is a rom storage of a program that gets copied into the beginning of +JSM-3 memory during boot. Different models of bootsticks were available for JSM-3, +with different models providing different rom capacity. The largest could store +2^15 word long programs. There were also 2^11, 2^12 and 2^14 bootstick models, +all compatible with Fun Machine. + +== JSM-3-Core + +CPU is directly connected to memory. CPU sends signals to +monitor module, that in turn can switch on/off lamps on the monitor. +CPU is capable of waiting for events on the input module, such as keyboard events or +lever update event. Input module is capable of storing 20 events in a queue-like electrical +scheme. Also, at any time CPU can read input signal from lever board. State of 16 levers +is described by 1 word. 2^16 words (16 bits) of ram are available to CPU. JSM-3-Core in +JSM-3 is itself controlled by "boot-chip". +After powering JSM-3-core, boot-chip temporarily takes control over memory card and starts +copying boot program word by word into the ram, until bootstick reports the end. +Physical interface of bootstick or bootstick reader won't be specified here. +After boot-chip is done, it gives start signal to JSM-3-Corem which starts it's timer. +Timer counts ticks, 80000 ticks per second. At the beginning of each tick CPU tries to +execute one instruction and move to the next. +JSM-3 lacks traditional caching. Instead, memory is divided into 3 level, based +on it's proximity to CPU. +- *M1*: Very fast. Addresses $[0, 2^8-1]$. Occupies first $2^8$ words. +- *M2*: Fast. Addresses $[2^8, 2^11-1]$. Fast and very fast levels occupy first $2^11$ words. +- *M3*: Slow. Addresses $[2^11, 2^14-1]$. Slow, fast and very fast levels occupy + first $2^14$ words. +- *M4*: Very slow. Addresses $[2^14, 2^161-]$. diff --git a/earth_doc/head.typ b/earth_doc/head.typ new file mode 100644 index 0000000..c96d391 --- /dev/null +++ b/earth_doc/head.typ @@ -0,0 +1,181 @@ +/* This was originally my head.typ file from 3rd semester of calculus. + * But I stripped all the math formatting stuff. + * */ + +#import calc + +#let VAR = [123123] + +#let norm-vec2(A) = calc.sqrt(A.at(0) * A.at(0) + A.at(1) * A.at(1)) + +#let rotate-vec2(V, angle) = ( + calc.cos(angle) * V.at(0) + calc.sin(angle) * V.at(1), + -calc.sin(angle) * V.at(0) + calc.cos(angle) * V.at(1) +) + +#let rotate-vec2-90deg(V) = (V.at(1), -V.at(0)) + +#let mul-vec2-scal(A, b) = (A.at(0) * b, A.at(1) * b) + +#let add-vec2(A, B) = (A.at(0) + B.at(0), A.at(1) + B.at(1)) + +#let sub-vec2(A, B) = (A.at(0) - B.at(0), A.at(1) - B.at(1)) + +#let make-length-vec2(fv) = mul-vec2-scal(fv, 1pt) + +#let draw-something(cnt) = place(top + left, dx: 0pt, dy: 0pt, cnt) + +#let draw-line(start, end, stroke: black) = draw-something( + line(start: make-length-vec2(start), end: make-length-vec2(end), stroke: stroke)) + +#let draw-arrow-head(start, end, arm-sz: 4.5, stroke: black) = { + let d = sub-vec2(start, end) + let L = norm-vec2(d) + let arrow_angle = calc.pi * 3/16 + let a1 = rotate-vec2(d, -arrow_angle) + let a2 = rotate-vec2(d, arrow_angle) + if (L > arm-sz){ + a1 = mul-vec2-scal(a1, arm-sz/L) + a2 = mul-vec2-scal(a2, arm-sz/L) + } + + let tg_end = make-length-vec2(end) + let tg_a1 = make-length-vec2(a1) + let tg_a2 = make-length-vec2(a2) + + show line: draw-something + line(start: add-vec2(tg_end, tg_a1), end: tg_end, stroke: stroke) + line(start: add-vec2(tg_end, tg_a2), end: tg_end, stroke: stroke) +} + +#let draw-arrow(start, end, stroke: black) = { + let tg_start = make-length-vec2(start) + let tg_end = make-length-vec2(end) + + show line: draw-something + line(start: tg_start, end:tg_end, stroke: stroke) + draw-arrow-head(start, end, stroke: stroke) +} + +#let draw-content(pos, cnt) = place(top + left, +dx: pos.at(0) * 1pt, dy: pos.at(1) * 1pt, cnt) + +#let linp(A1, B1, A2, B2, x) = (x - A1) * (B2 - A2) / (B1 - A1) + A2 + +#let CSCpos2pac(csc, pan_dim, pos) = ( + linp(csc.X_at_left, csc.X_at_right, 0, pan_dim.at(0), pos.at(0)), + linp(csc.Y_at_bottom, csc.Y_at_top, pan_dim.at(1), 0, pos.at(1)) +) + +#let draw-coordinate-system(csc, pan_dim, +Ox_marks: (), Oy_marks: (), x_name: $x$, y_name: $y$) = { + let pac_y_of_Ox = linp(csc.Y_at_top, csc.Y_at_bottom, 0, pan_dim.at(1), 0) + let Ox_enters_pac = (0, pac_y_of_Ox) + let Ox_leaves_pac = (pan_dim.at(0), pac_y_of_Ox) + let pac_x_of_Oy = linp(csc.X_at_left, csc.X_at_right, 0, pan_dim.at(0), 0) + let Oy_enters_pac = (pac_x_of_Oy, pan_dim.at(1)) + let Oy_leaves_pac = (pac_x_of_Oy, 0) + draw-arrow(Ox_enters_pac, Ox_leaves_pac) + draw-arrow(Oy_enters_pac, Oy_leaves_pac) + + let funny_offset_Ox = (-5, 2) + let funny_offset_Oy = (3, -6) + + for (val, cnt) in Ox_marks { + let pan_pac = CSCpos2pac(csc, pan_dim, (val, 0)) + draw-line(add-vec2(pan_pac, (0, 2)), add-vec2(pan_pac, (0, -2))) + draw-content(add-vec2(pan_pac, funny_offset_Ox), cnt) + } + for (val, cnt) in Oy_marks { + let pan_pac = CSCpos2pac(csc, pan_dim, (0, val)) + draw-line(add-vec2(pan_pac, (2, 0)), add-vec2(pan_pac, (-2, 0))) + draw-content(add-vec2(pan_pac, funny_offset_Oy), cnt) + } + draw-content(add-vec2(Ox_leaves_pac, funny_offset_Ox), x_name) + draw-content(add-vec2(Oy_leaves_pac, funny_offset_Oy), y_name) +} + +#let fix-csc(csc, pan_dim) = { + let X_at_left = csc.at("X_at_left", default: none) + let X_at_right = csc.at("X_at_right", default: none) + let Y_at_bottom = csc.at("Y_at_bottom", default: none) + let Y_at_top = csc.at("Y_at_top", default: none) + + let goodX = X_at_left != none and X_at_right != none + let goodY = Y_at_bottom != none and Y_at_top != none + if ((not goodX) and (not goodY)){ + panic("This is wrong") + } + if (goodX and goodY) { + + } else if (goodX){ + let yd = (X_at_right - X_at_left) * pan_dim.at(1) / pan_dim.at(0) + if (Y_at_bottom != none){ + csc.Y_at_top = Y_at_bottom + yd + } else if (Y_at_top != none){ + csc.Y_at_bottom = Y_at_top - yd + } else { + csc.Y_at_bottom = -yd/2 + csc.Y_at_top = yd/2 + } + } else if (goodY){ + let xd = (Y_at_top - Y_at_bottom) * pan_dim.at(0) / pan_dim.at(1) + if (X_at_left != none){ + csc.X_at_right = X_at_left + xd + } else if (A_at_right != none){ + csc.X_at_left = X_at_right - xd + } else { + csc.X_at_left = -xd/2 + csc.X_at_right = xd/2 + } + } + return csc +} + +// csc is linear coordinate system configurration for display on panel +#let plot-function-nocheck(csc, pan_dim, func, stroke: black+1pt) = { + let points = range(0, calc.floor(pan_dim.at(0)) + 1).map((x_pac) => { + let x = linp(0, pan_dim.at(0), csc.X_at_left, csc.X_at_right, x_pac) + let y = func(x) + let y_pac = linp(csc.Y_at_bottom, csc.Y_at_top, pan_dim.at(1), 0, y) + (x_pac * 1pt, y_pac * 1pt) + }) + draw-something(path(stroke: stroke, ..points)) +} + +#let plot-path-nocheck(csc, pan_dim, ..points, stroke: black) = { + draw-something(path(stroke: stroke, + ..(points).pos().map(point => + make-length-vec2(CSCpos2pac(csc, pan_dim, point))))) +} + +#let plot-closed-figure-nocheck(csc, pan_dim, ..points, stroke: black, fill: none) = { + draw-something(path(stroke: stroke, fill: fill, closed: true, + ..(points).pos().map(point => + make-length-vec2(CSCpos2pac(csc, pan_dim, point))))) +} + +#let ensureTypeRelative(x) = { + if type(x) == relative { + return x + } else if type(x) == ratio { + return x + 0pt + } else if type(x) == length { + return x + 0% + } + panic("Not supported") +} + +#let drawing-rectangle(ab_width, ab_height, drawing_function, stroke: none) = context { + let rl_width = ensureTypeRelative(ab_width) + let rl_height = ensureTypeRelative(ab_height) + layout(size => { + let dim = ( + (rl_width.ratio * size.width + rl_width.length.to-absolute()).pt(), + (rl_height.ratio * size.height + rl_height.length.to-absolute()).pt() + ) + + rect(width: rl_width, height: rl_height, inset: 0pt, + stroke: stroke, drawing_function(dim)) + }) +} diff --git a/earth_doc/test.typ b/earth_doc/test.typ new file mode 100644 index 0000000..9d05457 --- /dev/null +++ b/earth_doc/test.typ @@ -0,0 +1,6 @@ +#import "./head.typ": * + +Some text in line +#rect(stroke: none)[ + #draw-content((0, 0), line(start: (0pt, 20pt), end: (40pt, 0pt), stroke: black + 0.1pt)) +] diff --git a/src/l1/core/VecSpan_int_primitives.h b/src/l1/core/VecSpan_int_primitives.h index 65b9a1f..f04494a 100644 --- a/src/l1/core/VecSpan_int_primitives.h +++ b/src/l1/core/VecSpan_int_primitives.h @@ -26,6 +26,10 @@ void ConstSpanU8_print(ConstSpanU8 str) { putchar((int)*ConstSpanU8_at(str, i)); } +SpanT_VecT_trivmove_COMPLETE_Definition(U16) +VecT_primitive_zeroinit_method_Definition(U16) +SpanT_comparable_method_Definition(U16) + SpanT_VecT_trivmove_COMPLETE_Definition(U32) VecT_primitive_zeroinit_method_Definition(U32) SpanT_comparable_method_Definition(U32) diff --git a/src/l1/core/util.h b/src/l1/core/util.h index 19e85c1..383d54d 100644 --- a/src/l1/core/util.h +++ b/src/l1/core/util.h @@ -269,7 +269,8 @@ void SpanT##_sort(SpanT self) { \ #define SpanT_VecT_trivmove_COMPLETE_Definition(T) \ VecT_trivmove_struct_Definition(T) VecT_trivmove_method_Definition(T) \ - SpanT_struct_Definition(T) SpanT_method_Definition(T) SpanT_VecT_method_Definition(T) + SpanT_struct_Definition(T) SpanT_method_Definition(T) SpanT_VecT_method_Definition(T) \ + VecT_trivmove_equal_method_Definition(T) diff --git a/src/l2/margaret/margaret.h b/src/l2/margaret/margaret.h index f7a935b..0f71857 100644 --- a/src/l2/margaret/margaret.h +++ b/src/l2/margaret/margaret.h @@ -67,7 +67,7 @@ void margaret_win_init_set_properties(Xlib_Display* dpy, Xlib_Window win) { }; Atom atoms[3]; int status; - status = XInternAtoms(dpy, (strings), 3, False, atoms); + status = XInternAtoms(dpy, (char**)(strings), 3, False, atoms); if (status == 0) abortf("XInternAtoms"); status = XChangeProperty(dpy, win, atoms[0], atoms[1], 32, PropModeReplace, (unsigned char *)&atoms[2], 1); @@ -239,14 +239,6 @@ typedef struct { U32 for_presentation; } MargaretChosenQueueFamilies; -// MargaretChosenQueueFamilies MargaretChosenQueueFamilies_new() { -// return (MargaretChosenQueueFamilies){ .for_graphics = None_U32(), .for_presentation = None_U32() }; -// } - -// bool MargaretChosenQueueFamilies_is_complete(const MargaretChosenQueueFamilies* chosen_queue_families) { -// return OptionU32_is_some(&chosen_queue_families->for_graphics) && OptionU32_is_some(&chosen_queue_families->for_presentation); -// } - #define VkQueueFamilyProperties_drop(x) {} #define VkQueueFamilyProperties_clone(vp) (*(vp)) @@ -545,18 +537,13 @@ MargaretScoredPhysicalDevice margaret_score_physical_device( #define MargaretScoredPhysicalDevice_drop(x) {} #define MargaretScoredPhysicalDevice_clone(vp) (*(vp)) +#define MargaretScoredPhysicalDevice_equal_MargaretScoredPhysicalDevice(a, b) ((a) == (b)) #define MargaretScoredPhysicalDevice_less_MargaretScoredPhysicalDevice(cap, cbp) ((cap)->score < (cbp)->score) SpanT_VecT_trivmove_COMPLETE_Definition(MargaretScoredPhysicalDevice) VecT_primitive_zeroinit_method_Definition(MargaretScoredPhysicalDevice) SpanT_comparable_method_Definition(MargaretScoredPhysicalDevice) -// VecT_trivmove_struct_Definition(MargaretScoredPhysicalDevice) -// VecT_trivmove_method_Definition(MargaretScoredPhysicalDevice) -// SpanT_struct_Definition(MargaretScoredPhysicalDevice) -// SpanT_method_Definition(MargaretScoredPhysicalDevice) -// SpanT_VecT_method_Definition(MargaretScoredPhysicalDevice) - #define VkPhysicalDevice_drop(x) {} #define VkPhysicalDevice_clone(vp) (*(vp)) @@ -800,16 +787,19 @@ void MargaretSwapchainBundle_drop_with_device(VkDevice device, MargaretSwapchain } VkShaderModule margaret_VkShaderModule_new(VkDevice device, VecU8 code) { + if (code.len < 4) + abortf("Kill yourself, please\n"); VkShaderModuleCreateInfo shad_mod_crinfo = { .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = code.len, // Now this is funny, we can't put arbitrary byte-string here, it should be 4-byte aligned - // Thanks goodness all the strings in VecU8 are allocated with calloc, which gives high aligning to virtually everything + // Thanks goodness all the strings in VecU8 are allocated with calloc, which gives high alignment to + // virtually everything .pCode = (const uint32_t*)code.buf }; VkShaderModule shad_module; if (vkCreateShaderModule(device, &shad_mod_crinfo, NULL, &shad_module) != VK_SUCCESS) - abortf("vkCreateShaderModule"); + abortf("vkCreateShaderModule\n"); return shad_module; } @@ -921,8 +911,8 @@ uint32_t margaret_find_memory_type( abortf("Could not find a good memory type"); } -// Suppose the previous buffer ended at pos was. It may just so happen that `was` is aligned with the next buffer, -// but if it is not, for some reason, we skip a couple of bytes after was and return the smallest next aligned pos +// Suppose the previous buffer ended at pos `was`. It may just so happen that `was` is aligned with the next buffer, +// but if it is not, for some reason, we skip a couple of bytes after `was` and return the smallest next aligned pos VkDeviceSize margaret_align_start_of_buffer(VkDeviceSize was, VkDeviceSize alignment) { return was % alignment ? (was + alignment - was % alignment) : was; } @@ -1039,18 +1029,6 @@ VkDeviceMemory margaret_initialize_buffers_and_images( return memory; } -// Don't need that, we create staging (transfer_src_bit) the other way -// template -// BufferInMemoryInfo buffer_crinfo_of_staging_vbo(size_t n) { - // return BufferInMemoryInfo{.sz = sizeof(TV) * n, .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT }; -// } -// BufferInMemoryInfo buffer_crinfo_of_staging_ebo(size_t n) { -// return BufferInMemoryInfo{.sz = sizeof(uint32_t) * n, .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT }; -// } -// -// BufferInMemoryInfo buffer_crinfo_of_staging_texture_rgba(uint32_t w, uint32_t h) { -// return BufferInMemoryInfo{.sz = 4ull * w * h, .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT}; -// } #define margaret_prep_buffer_mem_info_of_gpu_vbo_Definition(TV) \ MargaretBufferInMemoryInfo TV##_buffer_crinfo_of_gpu_vbo(size_t n) { \ @@ -1070,12 +1048,12 @@ MargaretBufferInMemoryInfo margaret_prep_buffer_mem_info_of_small_local_ubo(size return (MargaretBufferInMemoryInfo){ .sz = struct_sz, .usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT }; } -MargaretImageInMemoryInfo image_crinfo_of_gpu_texture_rgba(uint32_t w, uint32_t h) { +MargaretImageInMemoryInfo margaret_prep_image_mem_info_of_gpu_texture_rgba(uint32_t w, uint32_t h) { return (MargaretImageInMemoryInfo){ .width = w, .height = h, .format = VK_FORMAT_R8G8B8A8_SRGB, .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT }; } -MargaretImageInMemoryInfo image_crinfo_of_zbuffer(uint32_t max_width, uint32_t max_height, VkFormat zbuf_format) { +MargaretImageInMemoryInfo margaret_prep_image_mem_info_of_zbuffer(uint32_t max_width, uint32_t max_height, VkFormat zbuf_format) { return (MargaretImageInMemoryInfo){ .width = max_width, .height = max_height, .format = zbuf_format, .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT }; } @@ -1321,30 +1299,36 @@ OptionVkFormat margaret_find_supported_zbuffer_format(VkPhysicalDevice physical_ (ConstSpanVkFormat){.data = candidates, .len = ARRAY_SIZE(candidates)}, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); } +#define VkDescriptorPoolSize_drop(v) {} +#define VkDescriptorPoolSize_clone(p) (*(p)) + +VecT_trivmove_struct_Definition(VkDescriptorPoolSize) +VecT_trivmove_method_Definition(VkDescriptorPoolSize) + VkDescriptorPool margaret_create_descriptor_set_pool(VkDevice device, uint32_t ubo_descriptor_count, uint32_t image_sampler_descriptor_count, uint32_t max_sets ) { - VkDescriptorPoolSize sizes[] = { - (VkDescriptorPoolSize){ + VecVkDescriptorPoolSize sizes = VecVkDescriptorPoolSize_new(); + if (ubo_descriptor_count > 0) + VecVkDescriptorPoolSize_append(&sizes, (VkDescriptorPoolSize){ .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = ubo_descriptor_count - }, - // todo: fix it - // (VkDescriptorPoolSize){ - // .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - // .descriptorCount = image_sampler_descriptor_count - // }, - }; - // todo: check for a case when image_sampler_descriptor_count or ubo_descriptor_count is zero + }); + if (image_sampler_descriptor_count > 0) + VecVkDescriptorPoolSize_append(&sizes, (VkDescriptorPoolSize){ + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = image_sampler_descriptor_count + }); VkDescriptorPoolCreateInfo crinfo = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, .maxSets = max_sets, - .poolSizeCount = ARRAY_SIZE(sizes), - .pPoolSizes = sizes, + .poolSizeCount = sizes.len, + .pPoolSizes = sizes.buf, }; VkDescriptorPool descriptor_pool; if (vkCreateDescriptorPool(device, &crinfo, NULL, &descriptor_pool) != VK_SUCCESS) abortf("vkCreateDescriptorPool"); + VecVkDescriptorPoolSize_drop(sizes); return descriptor_pool; } diff --git a/src/l2/tests/r0.c b/src/l2/tests/r0.c index b9cce49..cee00e1 100644 --- a/src/l2/tests/r0.c +++ b/src/l2/tests/r0.c @@ -205,7 +205,6 @@ PipelineHands create_graphics_pipeline( }; VkDescriptorSetLayoutBinding bindings_for_my_descr_set_layout[] = { - // some random binding { // Binding in shader .binding = 0, @@ -214,7 +213,7 @@ PipelineHands create_graphics_pipeline( .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, }, - // VkDescriptorSetLayoutBinding { + // { // .binding = 1, // .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // .descriptorCount = 1, @@ -240,7 +239,7 @@ PipelineHands create_graphics_pipeline( VkPipelineLayout pipeline_layout; if (vkCreatePipelineLayout(device, &layout_crinfo, NULL, &pipeline_layout) != VK_SUCCESS) abortf("vkCreatePipelineLayout"); - // todo: kill myself (update: still todo (update: still not done)) + // todo: kill myself (update: still todo (update: still not done )) update: work in progress VkGraphicsPipelineCreateInfo pipeline_crinfo = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .stageCount = ARRAY_SIZE(shader_stages_crinfo), @@ -265,7 +264,7 @@ PipelineHands create_graphics_pipeline( vkDestroyShaderModule(device, frag_module, NULL); vkDestroyShaderModule(device, vert_module, NULL); - return (PipelineHands){.pipeline_layout = pipeline_layout, .pipeline = pipeline ,.descriptor_set_layout = my_descriptor_set_layout}; + return (PipelineHands){.pipeline_layout = pipeline_layout, .pipeline = pipeline, .descriptor_set_layout = my_descriptor_set_layout}; } void reset_and_record_command_buffer( @@ -295,7 +294,6 @@ void reset_and_record_command_buffer( vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_and_layout->pipeline); // We forgot that viewport is not built into our pipeline - // printf("image_extent = {%u, %u}\n", image_extent.width, image_extent.height); VkViewport viewport = { .x = 0.0f, .y = 0.0f, @@ -338,7 +336,8 @@ void recreate_swapchain( VkSwapchainKHR old_swapchain = MargaretSwapchainBundle_pop_swapchain_drop_rest(device, *swfb); // old swfb is 83% dropped ResultMargaretChosenSwapchainDetailsOrConstSpanU8 swapchain_details_res = margaret_choose_swapchain_details(physical_device, surface); - assert(swapchain_details_res.variant == Result_Ok); + if (swapchain_details_res.variant != Result_Ok) + abortf("swapchain_details_res.variant != Result_Ok"); MargaretChosenSwapchainDetails swapchain_details = swapchain_details_res.ok; MargaretSwapchainBundle new_swfb = MargaretSwapchainBundle_new(device, queue_fam, swapchain_details, surface, render_pass, swfb->swapchain); @@ -352,17 +351,18 @@ margaret_prep_buffer_mem_info_of_gpu_vbo_Definition(OA_Vertex) void prepare_shaders() { int ret = system("./test_shader_compile.sh"); if (ret == -1) { - perror("system() failed"); - exit(EXIT_FAILURE); + abortf("system() failed\n"); } else if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0) { - fprintf(stderr, "Error: script exited with code %d\n", WEXITSTATUS(ret)); - exit(EXIT_FAILURE); + abortf("Error: script exited with code %d\n", WEXITSTATUS(ret)); } else if (!WIFEXITED(ret)) { - fprintf(stderr, "Error: script terminated abnormally\n"); - exit(EXIT_FAILURE); + abortf("Error: script terminated abnormally\n"); } } +typedef struct { + U8 r; U8 g; U8 b; U8 a; +} color8rgba; + int main() { prepare_shaders(); @@ -391,7 +391,8 @@ int main() { // print_physical_device_available_extensions(physical_device); ResultMargaretChosenQueueFamiliesOrConstSpanU8 queue_fam_res = margaret_choose_good_queue_families(physical_device, surface); - assert(queue_fam_res.variant == Result_Ok); + if (queue_fam_res.variant != Result_Ok) + abortf("queue_fam_res.variant != Result_Ok"); MargaretChosenQueueFamilies queue_fam = queue_fam_res.ok; VkDevice device = margaret_create_logical_device(physical_device, queue_fam); @@ -402,7 +403,8 @@ int main() { vkGetDeviceQueue(device, queue_fam.for_graphics, 0, &presentation_queue); ResultMargaretChosenSwapchainDetailsOrConstSpanU8 swapchain_details_res = margaret_choose_swapchain_details(physical_device, surface); - assert(swapchain_details_res.variant == Result_Ok); + if (swapchain_details_res.variant != Result_Ok) + abortf("swapchain_details_res.variant != Result_Ok"); MargaretChosenSwapchainDetails swapchain_details = swapchain_details_res.ok; // We hope that the image format won't be changed even when window gets resized @@ -414,18 +416,36 @@ int main() { MargaretSwapchainBundle swfb = MargaretSwapchainBundle_new(device, queue_fam, swapchain_details, surface, render_pass, NULL); // Filling scene info - OA_Vertex obj1_vertexes[] = { + const OA_Vertex obj1_vertexes[] = { (OA_Vertex){ .pos = {-0.6f, -1.0f, 0}, .color = {1.f, 0, 0} }, (OA_Vertex){ .pos = {-0.8f, -0.8f, 0}, .color = {0, 1.f, 0} }, (OA_Vertex){ .pos = {-0.8f, -0.6f, 0}, .color = {0, 0, 1.f} }, }; - uint32_t obj1_indices[] = { 0, 1, 2 }; - OA_Vertex obj2_vertexes[] = { + const uint32_t obj1_indices[] = { 0, 1, 2 }; + const OA_Vertex obj2_vertexes[] = { (OA_Vertex){ .pos = {0.9f, 0.9f}, .color = {1.f, 0, 0} }, (OA_Vertex){ .pos = {0.4f, -0.9f, 0}, .color = {0, 1.f, 0} }, (OA_Vertex){ .pos = {-0.2f, 1.f}, .color = {0, 0, 1.f} }, }; - uint32_t obj2_indices[] = {0, 1, 2}; + const uint32_t obj2_indices[] = {0, 1, 2}; + + const U32 wood_texture_width = 100; + const U32 wood_texture_height = 100; + color8rgba wood_texture_data[wood_texture_height][wood_texture_width]; + for (U32 y = 0; y < wood_texture_height; y++) { + for (U32 col = 0; col < wood_texture_width; col++) { + wood_texture_data[y][col] = (color8rgba){150, 30, 50, 255}; + } + } + for (U32 i = 0; i < 10; i++) { + for (U32 y = 0; y < wood_texture_height; y++) { + U32 col = 3 + i * 10 + ((30 < y + 3 * i && y - i < 60) ? 1 : 0); + wood_texture_data[y][col] = (color8rgba){130, 25, 40, 255}; + wood_texture_data[y][col + 1] = (color8rgba){80, 10, 15, 255}; + wood_texture_data[y][col + 2] = (color8rgba){70, 11, 12, 255}; + wood_texture_data[y][col + 3] = (color8rgba){125, 20, 20, 255}; + } + } // todo: add a texture into the mix // We have only one staging buffer in host memory (because we don't really need more) @@ -434,7 +454,8 @@ int main() { MAX_U64(sizeof(obj1_indices), MAX_U64(sizeof(obj2_vertexes), MAX_U64(sizeof(obj2_indices), - MAX_U64(sizeof(MyUbo), 0))))) + MAX_U64(sizeof(MyUbo), + MAX_U64(sizeof(wood_texture_data), 0)))))) , .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT }; VkDeviceMemory host_mem = margaret_initialize_buffers_and_images(physical_device, device, (SpanMargaretBufferInMemoryInfo){.data = &host_mem_buffer, .len = 1}, (SpanMargaretImageInMemoryInfo){ 0 }, @@ -447,6 +468,9 @@ int main() { margaret_prep_buffer_mem_info_of_gpu_ebo(ARRAY_SIZE(obj2_indices)), margaret_prep_buffer_mem_info_of_gpu_ubo(sizeof(MyUbo)), }; + MargaretImageInMemoryInfo device_mem_images[] = { + margaret_prep_image_mem_info_of_gpu_texture_rgba(wood_texture_width, wood_texture_height) + }; VkDeviceMemory device_mem = margaret_initialize_buffers_and_images(physical_device, device, (SpanMargaretBufferInMemoryInfo){ .data = device_mem_buffers, .len = ARRAY_SIZE(device_mem_buffers)}, (SpanMargaretImageInMemoryInfo){ 0 }, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); @@ -488,7 +512,7 @@ int main() { .vbo = device_vbo_1_buffer.buffer, .ebo = device_ebo_1_buffer.buffer, .vert_count = ARRAY_SIZE(obj1_vertexes) }; *VecOA_ObjectOnScene_at(&scene.oa_objects, 1) = (OA_ObjectOnScene){ .vbo = device_vbo_2_buffer.buffer, .ebo = device_ebo_2_buffer.buffer, .vert_count = ARRAY_SIZE(obj2_vertexes) }; - // device_vob/ebo_1/2_buffer won't be used anymore + // device_vbo/ebo_1/2_buffer won't be used only intrinsically by vulkan, not by us VkDescriptorPool descriptor_pool = margaret_create_descriptor_set_pool(device, 1, 0, 1); VkDescriptorSetAllocateInfo descriptor_sets_alloc_info = { diff --git a/src/l2/tests/test_shaders/glsl/0/0.vert b/src/l2/tests/test_shaders/glsl/0/0.vert index c14220b..b53aee6 100644 --- a/src/l2/tests/test_shaders/glsl/0/0.vert +++ b/src/l2/tests/test_shaders/glsl/0/0.vert @@ -5,18 +5,6 @@ layout(location = 1) in vec3 color; layout(location = 0) out vec3 vsout_color; -vec2 positions[3] = vec2[]( - vec2(0.0, -0.5), - vec2(0.5, 0.5), - vec2(-0.5, 0.5) -); - -vec3 colors[3] = vec3[]( - vec3(1.0, 0.0, 0.0), - vec3(0.0, 1.0, 0.0), - vec3(0.0, 0.0, 1.0) -); - void main() { gl_Position = vec4(pos, 1.0); vsout_color = color; diff --git a/src/l3/fun_machine/bubina.h b/src/l3/fun_machine/bubina.h new file mode 100644 index 0000000..52e4159 --- /dev/null +++ b/src/l3/fun_machine/bubina.h @@ -0,0 +1,8 @@ +#ifndef PROTOTYPE1_SRC_L3_FUN_MACHINE_BUBINA_H +#define PROTOTYPE1_SRC_L3_FUN_MACHINE_BUBINA_H + +typedef struct { + +} BubinaState; + +#endif diff --git a/src/l3/fun_machine/fun_machine.h b/src/l3/fun_machine/fun_machine.h new file mode 100644 index 0000000..a6fd41b --- /dev/null +++ b/src/l3/fun_machine/fun_machine.h @@ -0,0 +1,187 @@ +#ifndef PROTOTYPE1_SRC_L3_FUN_MACHINE_STATE_H +#define PROTOTYPE1_SRC_L3_FUN_MACHINE_STATE_H + +#include "../../l1/core/util.h" +#include "../../l1/core/VecSpan_int_primitives.h" + +// todo: recheck this structure +const U8 FunMachine_LRU_states[24][4] ={ + /* x = 0 1 2 3 */ + { 3, 0, 8, 16}, /* state 0 : (1 2 3 0) LRU=0 */ + { 1, 11, 9, 17}, /* state 1 : (0 2 3 1) LRU=1 */ + { 2, 10, 19, 18}, /* state 2 : (0 1 3 2) LRU=2 */ + { 3, 11, 19, 18}, /* state 3 : (0 1 2 3) LRU=3 */ + { 2, 4, 8, 16}, + { 5, 10, 9, 17}, + { 6, 10, 9, 18}, + { 7, 11, 19, 17}, + { 7, 0, 8, 20}, + { 1, 15, 9, 21}, + { 2, 10, 23, 22}, + { 3, 11, 23, 22}, + { 1, 0, 12, 20}, + { 1, 0, 13, 21}, + { 2, 14, 8, 22}, + { 3, 15, 23, 16}, + { 6, 4, 12, 16}, + { 5, 14, 13, 17}, + { 6, 14, 13, 18}, + { 7, 15, 19, 21}, + { 5, 4, 12, 20}, + { 5, 4, 13, 21}, + { 6, 14, 12, 22}, + { 7, 15, 23, 20} +}; + +const int FunMachine_levers_count = 16; +const int FunMachine_keys_count = 50; + +const int FunMachine_cache_banks_count = 4; +const int FunMachine_cache_sets_count = 32; +const int FunMachine_cache_sets_pow = 5; +const int FunMachine_cache_line_size = 16; +const int FunMachine_cache_line_pow = 4; + +const int FunMachine_disk_drives = 2; +const int FunMachine_disk_io_block_pow = 7; +const int FunMachine_disk_io_block_size = 128; + +typedef struct { + U64 timeout_remaining; + bool powered; + U16 levers; + bool keys[FunMachine_keys_count]; + // History of cache bank usage (from 0 to 23) + U8 lru = 0; + // Our simulation acknowledges complete cache consistency and does not separate cache storage from + // memory storage + // We have 4 banks and memory blocks of size 2^4 (we have 2^12) of them are separated into + // 2^5 interleaved sets (at each time each set can point to 4 of 2^7 possible indexes) + U8 cache_indexes[FunMachine_cache_banks_count][FunMachine_cache_sets_count]; + // We store the last cache line that was used for instruction decoding right inside JSM3C + U16 last_command_line_mask; // Last 4 bits in IP are zeroed out + + // We store the history of 3 + // 0 - Z (all bits of last op result were 0) + // 1 - S (msb of last op result was 1) + // 2 - O (last operation resulted in overflow) + U16 flags; + U16 AX; + U16 BX; + U16 CX; + U16 DX; + U16 EX; + U16 FX; + U16 IP; + VecU16 memory; +} FunMachineState; + +FunMachineState FunMachineState_from_image(ConstSpanU16 image) { + assert(image.len <= UINT16_MAX); + FunMachineState res = (FunMachineState){ + .timeout_remaining = UINT64_MAX, + .memory = VecU16_new_zeroinit(UINT16_MAX)}; + return res; +} + +void FunMachine_drop(FunMachineState self) { + VecU16_drop(self.memory); +} + +typedef enum { + FunMachineIM_power_on, + FunMachineIM_power_off, + FunMachineIM_auto_wake, + FunMachineIM_button_press, + FunMachineIM_button_release, + FunMachineIM_lever_changed, + FunMachineIM_disk_drive_connected, + FunMachineIM_disk_drive_disconnected, + FunMachineIM_disk_drive_not_busy, + FunMachineIM_disk_drive_synchronized, +} FunMachineIM_variant; + +typedef struct { + U8 drive_id; // From +} FunMachine_DriveConnectionInfo; + +typedef struct { + FunMachineIM_variant variant; + union { + U8 id; // For button and for lever event + FunMachine_DriveConnectionInfo drive; // For all the disk drive events + }; +} FunMachineIM; + +// (U64, funMachineIM) +typedef struct{ + U64 tp; + FunMachineIM im; +} FunMachineIM_TP; + +typedef enum { + FunMachineIMR_None, + FunMachineIMR_LampUpdate, + FunMachineIMR_ShutAllDown, +} FunMachineIMR_variant; + +typedef struct { + FunMachineIMR_variant variant; + // For FunMachineIMR_LampUpdate + U8 changed_row; + U16 nav; // new value set for that row +} FunMachineIMR; + +// (FunMachineIMR, int, U64) +typedef struct { + FunMachineIMR r; + /* Right now there is only one inner hardware error: + * if small physical interrupt queue overflows and some events get chewed up */ + int inner_hardware_error; + U64 awt; +} FunMachineIMR_AWT; + +// 80K TPS +const U64 tick_time = 12500; + +void FunMachine_boot(FunMachineState* self, ConstSpanU16 image) { + assert(image.len <= UINT16_MAX); + self->powered = true; + self->AX = self->BX = self->CX = self->DX = self->EX = self->FX = self->IP = self->flags = 0; + self->lru = 0; + self->last_command_line_mask = 0; + memcpy(self->memory.buf, image.data, image.len * sizeof(U16)); + memset(self->memory.buf + image.len, 0, (UINT16_MAX - image.len) * sizeof(U16)); + for (int i = 0; i < FunMachine_cache_banks_count; i++) + for (int j = 0; j < FunMachine_cache_sets_count; j++) + self->cache_indexes[i][j] = i; + +} + +void FunMachine_sync_time_progress(FunMachineState* self, U64 tp) { + assert(self->timeout_remaining >= tp); + if (self->timeout_remaining < UINT64_MAX) + self->timeout_remaining -= tp; +} + +FunMachineIMR_AWT FunMachine_execute_instruction(FunMachineState* self) { + // todo: actaully write this function + return (FunMachineIMR_AWT){ .r.variant = FunMachineIMR_None, .awt = self->timeout_remaining }; +} + +FunMachineIMR_AWT FunMachine_runtime_reaction(FunMachineState* self) { + if (self->timeout_remaining) + return (FunMachineIMR_AWT){ .r.variant = FunMachineIMR_None, .awt = self->timeout_remaining }; + return FunMachine_execute_instruction(self); +} + +FunMachineIMR_AWT FunMachine_act(FunMachineState* self, FunMachineIM_TP imtp) { + FunMachineIM im = imtp.im; + FunMachine_sync_time_progress(self, imtp.tp); + if (im.variant == FunMachineIM_power_off && !self->powered) + return (FunMachineIMR_AWT){ .r.variant = FunMachineIMR_None, .awt = self->timeout_remaining }; + // todo: actaully write this function + return (FunMachineIMR_AWT){ .r.variant = FunMachineIMR_None, .awt = self->timeout_remaining }; +} + +#endif diff --git a/src/l3/tests/p0.c b/src/l3/tests/p0.c new file mode 100644 index 0000000..6fee0dd --- /dev/null +++ b/src/l3/tests/p0.c @@ -0,0 +1,355 @@ +/* This shit was written by chatgpt */ + +#include "../fun_machine/fun_machine.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static void enter_altscreen(void) +{ + const char *smcup = tigetstr("smcup"); + if (smcup) + putp(smcup); +} + +static void leave_altscreen(void) +{ + const char *rmcup = tigetstr("rmcup"); + if (rmcup) + putp(rmcup); +} + +#define BOARD_SIZE 32 +#define BOARD_OUTER_SIZE (BOARD_SIZE + 2) +#define TIMEOUT_USEC 100000 // Won't be used + +#define LEVER_COUNT 16 +#define KEYBOARD_ROW_SIZE 10 +#define KEYBOARD_ROWS 5 +const short keyboard_style[KEYBOARD_ROWS] = {2, 1, 0, 2, 1}; + +typedef struct { + int lamps[BOARD_SIZE][BOARD_SIZE]; + U16 levers; // MSB is the left one, LSB is thr right one. false is down, true is up + + /* Numeration of key statuses in this array: + * 0 1 ... 9 + * 10 11 ... 19 + * 20 ... 29 + * 30 ... 39 + * 40 ... 49 + */ + U8 keys[KEYBOARD_ROW_SIZE * KEYBOARD_ROWS]; // 0 - released, 1 - pressed + bool on; // false - machine is off, on - machine is on +} VisibleState; + +VisibleState VisibleState_new() { + return (VisibleState){0}; +} + +/* Pos on screen is opposite to bit pos (we represent lever word in big endian) */ +void VisibleState_set_lever(VisibleState* self, int pos, bool v) { + U16 mask = (1u << (LEVER_COUNT - 1 - pos)); + self->levers = (self->levers & (~mask)) | ((short)v << LEVER_COUNT - 1 - pos); +} + +bool VisibleState_get_lever(VisibleState* self, int pos) { + return self->levers & (1u << (LEVER_COUNT - 1 - pos)); +} + +const char key_marks[KEYBOARD_ROW_SIZE * KEYBOARD_ROWS] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '?', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'p', '"', + '$', 'f', 'g', 'h', 'j', 'k', 'l', 'i', 'o', '\'', + '%', 'a', 's', 'd', 'z', 'x', 'c', 'v', 'n', 'm', + '*', 'b', '!', '/', ' ', '(', ')', '=', '-', '+', +}; + +#define LOGO_WIDTH 5 +#define LOGO_HEIGHT (7 + 2 + 7) + +U8 logo_bitmap[LOGO_HEIGHT][LOGO_WIDTH] = { + {1, 1, 1, 1, 1}, + {1, 0, 0, 0, 0}, + {1, 0, 0, 0, 0}, + {1, 1, 1, 1, 1}, + {1, 0, 0, 0, 0}, + {1, 0, 0, 0, 0}, + {1, 0, 0, 0, 0}, + + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + + {1, 1, 0, 1, 1}, + {1, 0, 1, 0, 1}, + {1, 0, 1, 0, 1}, + {1, 0, 1, 0, 1}, + {1, 0, 0, 0, 1}, + {1, 0, 0, 0, 1}, + {1, 0, 0, 0, 1}, +}; + +char engraving_char = '#'; +// char engraving_char = 176; // can be 177 or 178s + +typedef enum { + pair_lampboard_border = 1, + pair_unlit_lamp, + pair_lit_lamp, + pair_lever, + pair_released_key_border, + pair_released_key_symbol, + pair_pressed_key_border, + pair_pressed_key_symbol, + pair_power_button_outer, + pair_power_button_inner, + pair_unlit_led, + pair_lit_led, + pair_engraving +} ncurses_color_pairs; + +static void init_colors(void) +{ + start_color(); + use_default_colors(); + + init_pair(pair_lampboard_border, -1, 245); + init_pair(pair_unlit_lamp, 236, -1); + init_pair(pair_lit_lamp, COLOR_WHITE, COLOR_YELLOW); + init_pair(pair_lever, COLOR_WHITE, -1); + init_pair(pair_released_key_border, 233, 239); + init_pair(pair_released_key_symbol, 7, 0); + init_pair(pair_pressed_key_border, 237, 245); + init_pair(pair_pressed_key_symbol, 203, 88); + init_pair(pair_power_button_outer, 124, 9); + init_pair(pair_power_button_inner, COLOR_BLACK, 160); + init_pair(pair_unlit_led, 52, -1); + init_pair(pair_lit_led, COLOR_WHITE, 9); + init_pair(pair_engraving, 136, -1); +} + +/*--------------------------------------------------------------*/ +// Won't be used +static void seed_dots(VisibleState* state) +{ + for (int y = 0; y < BOARD_SIZE; ++y) + for (int x = 0; x < BOARD_SIZE; ++x) + state->lamps[y][x] = rand() & 1; +} + +static void toggle_dot(VisibleState* state, int y, int x) +{ + if (y >= 0 && y < BOARD_SIZE && x >= 0 && x < BOARD_SIZE) + state->lamps[y][x] ^= 1; +} + +/*--------------------------------------------------------------* + * Drawing + *--------------------------------------------------------------*/ +static void draw_board(VisibleState* state, int X, int Y) +{ + attron(COLOR_PAIR(pair_lampboard_border)); + for (int i = 0; i < BOARD_OUTER_SIZE; i++) { + mvaddch(Y, X + i, ' '); + mvaddch(Y + BOARD_OUTER_SIZE - 1, X + i, ' '); + mvaddch(Y + i, X, ' '); + mvaddch(Y + i, X + BOARD_OUTER_SIZE - 1, ' '); + } + + for (int y = 0; y < BOARD_SIZE; ++y) + for (int x = 0; x < BOARD_SIZE; ++x) { + int pair = state->lamps[y][x] ? pair_lit_lamp : pair_unlit_lamp; + attron(COLOR_PAIR(pair)); + mvaddch(Y + y + 1, X + x + 1, '.'); + attroff(COLOR_PAIR(pair)); + } + + refresh(); +} + +static void draw_power_button(int X, int Y) { + attron(COLOR_PAIR(pair_power_button_outer)); + mvaddch(Y + 0, X + 1, '-'); + mvaddch(Y + 0, X + 2, '-'); + mvaddch(Y + 0, X + 3, '-'); + mvaddch(Y + 1, X + 3, ' '); + mvaddch(Y + 1, X + 4, '|'); + mvaddch(Y + 2, X + 4, '|'); + mvaddch(Y + 3, X + 4, '|'); + mvaddch(Y + 3, X + 3, ' '); + mvaddch(Y + 4, X + 3, '-'); + mvaddch(Y + 4, X + 2, '-'); + mvaddch(Y + 4, X + 1, '-'); + mvaddch(Y + 4, X + 1, '-'); + mvaddch(Y + 3, X + 1, ' '); + mvaddch(Y + 3, X + 0, '|'); + mvaddch(Y + 2, X + 0, '|'); + mvaddch(Y + 1, X + 0, '|'); + mvaddch(Y + 1, X + 1, ' '); + attron(COLOR_PAIR(pair_power_button_inner)); + mvaddch(Y + 1, X + 2, ' '); + mvaddch(Y + 2, X + 1, ' '); + mvaddch(Y + 3, X + 2, ' '); + mvaddch(Y + 2, X + 3, ' '); + mvaddch(Y + 2, X + 2, 'P'); +} + +void draw_power_led(VisibleState* state, int X, int Y) { + attron(COLOR_PAIR(state->on ? pair_lit_led : pair_unlit_led)); + mvaddch(Y, X, state->on ? '.' : 'O'); +} + +void draw_levers(VisibleState* state, int X, int Y) { + attron(COLOR_PAIR(pair_lever)); + for (int i = 0; i < LEVER_COUNT; i++) { + mvaddch(Y + 1, X + 2 * i, 'O'); + mvaddch(Y + (VisibleState_get_lever(state, i) ? 0 : 2), X + 2 * i, '|'); + } +} + +void draw_keyboard(VisibleState* state, int X, int Y) { + for (int row = 0; row < KEYBOARD_ROWS; row++) { + for (int s = 0; s < KEYBOARD_ROW_SIZE; s++) { + int x = X + keyboard_style[row] + 4 * s; + int y = Y + row * 2; + bool pressed = state->keys[KEYBOARD_ROW_SIZE * row + s]; + attron(COLOR_PAIR(pressed ? pair_pressed_key_border : pair_released_key_border)); + mvaddch(y, x, '['); + mvaddch(y, x + 2, ']'); + attron(COLOR_PAIR(pressed ? pair_pressed_key_symbol : pair_released_key_symbol)); + mvaddch(y, x + 1, key_marks[KEYBOARD_ROW_SIZE * row + s]); + } + } +} + +void draw_logo_engraving(int X, int Y) { + attron(COLOR_PAIR(pair_engraving)); + for (int y = 0; y < LOGO_HEIGHT; y++) { + for (int x = 0; x < LOGO_WIDTH; x++) { + if (logo_bitmap[y][x]) + mvaddch(Y + y, X + x, engraving_char); + } + } +} + +#define LAMPBOARD_X 16 +#define LAMPBOARD_Y 0 +#define POWER_BUTTON_X 0 +#define POWER_BUTTON_Y (BOARD_OUTER_SIZE + 1) +#define POWER_LED_X (5 + 2) +#define POWER_LED_Y 32 +#define LEVER_ROW_X (5 + 12) +#define LEVER_ROW_Y (BOARD_OUTER_SIZE + 1) +#define KEYBOARD_X 10 +#define KEYBOARD_Y 40 +#define LOGO_X 3 +#define LOGO_Y 2 + +static void draw_fun_machine(VisibleState* state) { + erase(); + draw_board(state, LAMPBOARD_X, LAMPBOARD_Y); + draw_power_button(POWER_BUTTON_X, POWER_BUTTON_Y); + draw_power_led(state, POWER_LED_X, POWER_LED_Y); + draw_levers(state, LEVER_ROW_X, LEVER_ROW_Y); + draw_keyboard(state, KEYBOARD_X, KEYBOARD_Y); + draw_logo_engraving(LOGO_X, LOGO_Y); +} + +int lever_intersection(int evx, int evy) { + for (int i = 0; i < LEVER_COUNT; i++) { + int lcx = LEVER_ROW_X + 2 * i; + int lcy = LEVER_ROW_Y + 1; + if (evx == lcx && abs(lcy - evy) <= 1) + return i; + } + return -1; +} + +int key_intersection(int evx, int evy) { + for (int row = 0; row < KEYBOARD_ROWS; row++) { + for (int s = 0; s < KEYBOARD_ROW_SIZE; s++) { + int kcx = KEYBOARD_X + keyboard_style[row] + 4 * s + 1; + int kcy = KEYBOARD_Y + 2 * row; + if (abs(kcx - evx) <= 1 && kcy == evy) + return row * KEYBOARD_ROW_SIZE + s; + } + } + return -1; +} + +/*--------------------------------------------------------------* + * Main + *--------------------------------------------------------------*/ +static volatile sig_atomic_t keep_running = 1; +static void sigint_handler(int sig) { (void)sig; keep_running = 0; } + +int main(void) +{ + srand((unsigned)time(NULL)); + VisibleState mach_disp = VisibleState_new(); + seed_dots(&mach_disp); + + setupterm(NULL, fileno(stdout), NULL); + enter_altscreen(); + atexit(leave_altscreen); + + initscr(); + cbreak(); + noecho(); + keypad(stdscr, TRUE); + mousemask(ALL_MOUSE_EVENTS, NULL); + curs_set(0); + + init_colors(); + + signal(SIGINT, sigint_handler); + + timeout(TIMEOUT_USEC / 1000); + + while (keep_running) { + draw_fun_machine(&mach_disp); + + int ch = getch(); + if (ch == ERR) + continue; /* timeout */ + if (ch == 'q' || ch == 'Q') + break; /* quit */ + if (ch == KEY_MOUSE) { + MEVENT ev; + if (getmouse(&ev) == OK) { + if (ev.bstate & BUTTON1_CLICKED) { + toggle_dot(&mach_disp, ev.y - LAMPBOARD_Y - 1, ev.x - LAMPBOARD_X - 1); + int li = lever_intersection(ev.x, ev.y); + if (li >= 0) + VisibleState_set_lever(&mach_disp, li, !VisibleState_get_lever(&mach_disp, li)); + int ki = key_intersection(ev.x, ev.y); + if (ki >= 0) { + // do something + } + } else if (ev.bstate & BUTTON3_CLICKED) { + int ki = key_intersection(ev.x, ev.y); + if (ki >= 0) { + if (mach_disp.keys[ki]) { + // Releasing + mach_disp.keys[ki] = 0; + } else { + // Pressing + mach_disp.keys[ki] = 1; + } + } + } + } + + } else { + seed_dots(&mach_disp); /* any other key – scramble */ + } + } + + endwin(); + return 0; +}