some cleanup in rendering code. Prepared to write emulator of JSM3

This commit is contained in:
Андреев Григорий 2025-07-18 19:00:26 +03:00
parent e05eb37c79
commit 2f1dd814b6
12 changed files with 875 additions and 79 deletions

View File

@ -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})

View File

@ -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-]$.

181
earth_doc/head.typ Normal file
View File

@ -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))
})
}

6
earth_doc/test.typ Normal file
View File

@ -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))
]

View File

@ -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)

View File

@ -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)

View File

@ -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<typename TV>
// 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;
}

View File

@ -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 = {

View File

@ -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;

View File

@ -0,0 +1,8 @@
#ifndef PROTOTYPE1_SRC_L3_FUN_MACHINE_BUBINA_H
#define PROTOTYPE1_SRC_L3_FUN_MACHINE_BUBINA_H
typedef struct {
} BubinaState;
#endif

View File

@ -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

355
src/l3/tests/p0.c Normal file
View File

@ -0,0 +1,355 @@
/* This shit was written by chatgpt */
#include "../fun_machine/fun_machine.h"
#include <ncurses.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdbool.h>
#include <term.h>
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;
}