some cleanup in rendering code. Prepared to write emulator of JSM3
This commit is contained in:
parent
e05eb37c79
commit
2f1dd814b6
@ -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})
|
||||
|
||||
56
earth_doc/eng_FunMachine_description.typ
Normal file
56
earth_doc/eng_FunMachine_description.typ
Normal 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
181
earth_doc/head.typ
Normal 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
6
earth_doc/test.typ
Normal 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))
|
||||
]
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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 = {
|
||||
|
||||
@ -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;
|
||||
|
||||
8
src/l3/fun_machine/bubina.h
Normal file
8
src/l3/fun_machine/bubina.h
Normal 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
|
||||
187
src/l3/fun_machine/fun_machine.h
Normal file
187
src/l3/fun_machine/fun_machine.h
Normal 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
355
src/l3/tests/p0.c
Normal 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;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user