n-channel

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit a6f143e16da19f01d0b1d60f5d03e88ec7d715a3
parent dbeda96e92915990c3c47f3032de3ca6d350901d
Author: Samdal <samdal@protonmail.com>
Date:   Fri, 28 Feb 2025 16:31:48 +0100

change code font, add syntax highlighting

Diffstat:
M_config.yml | 4++++
M_posts/2024-01-20-Simplifying-state.md | 78+++++++++++++++++++++++++++++++++++++++---------------------------------------
M_posts/2024-05-08-VGA-Kontroller.md | 38+++++++++++++++++++-------------------
M_posts/2025-02-20-hello-world.md | 2+-
M_posts/2025-02-22-implications-of-OOP.md | 34+++++++++++++++++-----------------
M_posts/2025-02-22-making-generic-data-structures-in-C.md | 22+++++++++++-----------
M_posts/2025-02-23-my-old-projects.md | 4++--
M_sass/_main.scss | 17++++++++++++++---
M_todo.md | 13+++++++++----
Aassets/css/vs.css | 32++++++++++++++++++++++++++++++++
Aassets/fonts/Iosevka-Etoile.ttf | 0
11 files changed, 148 insertions(+), 96 deletions(-)

diff --git a/_config.yml b/_config.yml @@ -50,3 +50,7 @@ defaults: type: "posts" values: layout: "post" + +markdown: kramdown +highlighter: rouge +input: GFM diff --git a/_posts/2024-01-20-Simplifying-state.md b/_posts/2024-01-20-Simplifying-state.md @@ -13,13 +13,13 @@ Lots of code, contains a ton of state. My current project is especially prone du The problem that arises, is as the codebase grows, you naturally end up having multiple systems interacting with each-other. -Now in my special case, everything is completely compiletime known. Meaning there isn’t really any dynamic modularity to handle, but that makes this example even easier to show. +Now in my special case, everything is completely compiletime known. Meaning there isn't really any dynamic modularity to handle, but that makes this example even easier to show. -We’re going to look at one particular approach to solving complex communication between different “entities”, inspired by my experiences from writing hardware systems. +We're going to look at one particular approach to solving complex communication between different "entities", inspired by my experiences from writing hardware systems. ## Just use if statements -``` -#include “room1.h” +```c +#include "room1.h" void update() { if (room1.lever1) { @@ -29,11 +29,11 @@ void update() { } } ``` -Some might think the above code is too simple, or silly. One might say “It’s natural for most systems to grow to some complexity level where you need handlers, objects, etc. globals are bad for extensibility“. +Some might think the above code is too simple, or silly. One might say "It's natural for most systems to grow to some complexity level where you need handlers, objects, etc. globals are bad for extensibility". However, I think this is blatantly false. If you want to get work done, you better try to write things in a way which is this simple. Over-complicating things is a super easy trap to fall into. -``` +```c if (((room1.lever1 || room1.lever2) && (room2.lever1 && room2.lever4)) || secret_room.master_lever) { @@ -42,14 +42,14 @@ However, I think this is blatantly false. If you want to get work done, you bett door.state = CLOSED; } ``` -As things get more complex, don’t add some weird system, just continue to do simple things. Imagine writing the snippet above using said weird system where you push changes, or communicate via functions. -If possible you want `door.state` to only be changed at one place. This has the added benefit of being extremely readable. However, you will loose context other places, but if you’re good at grepping that shouldn’t be a problem. +As things get more complex, don't add some weird system, just continue to do simple things. Imagine writing the snippet above using said weird system where you push changes, or communicate via functions. +If possible you want `door.state` to only be changed at one place. This has the added benefit of being extremely readable. However, you will loose context other places, but if you're good at grepping that shouldn't be a problem. -*Even in situations where less information is known at compiletime, this can be applied in some form. For example, if you don’t know which switches are bound to which doors it’s generally solved by iterating through some datastructures. It’s worth noting that in dynamic cases, you don’t usually have lots of weird cases, they tend to be more predictable.* +*Even in situations where less information is known at compiletime, this can be applied in some form. For example, if you don't know which switches are bound to which doors it's generally solved by iterating through some datastructures. It's worth noting that in dynamic cases, you don't usually have lots of weird cases, they tend to be more predictable.* -## Wait, all logic inside “update()”? +## Wait, all logic inside "update()"? -Yes, that’s right. Just run all your logic each frame. Computers are blazingly fast, checking some if statements doesn’t take that long. You’re probably going to be limited by other things before a bunch of branches limit you. +Yes, that's right. Just run all your logic each frame. Computers are blazingly fast, checking some if statements doesn't take that long. You're probably going to be limited by other things before a bunch of branches limit you. Compared to signals, event systems, and other methods of solving this problem, just having an update functions allows a lot more flexibility. Using event systems leads to exponentially more code as you add states and signals. A lot of them also use string identifiers, and dynamic access. Which is extremely bothersome for extensibility, and is prone to typos. @@ -66,7 +66,7 @@ You can create a corutine instance for each of your update() functions. This lets you store code state without having to write lots of code managing it. Personally I use [minicoro](https://github.com/edubart/minicoro) to do my corutine states. -``` +```c void update(coro_t* co) { while (true) { if (room1.lever1) break; @@ -87,13 +87,13 @@ void update(coro_t* co) { } ``` -I’m hoping this simple example sort of shows how extensible and concise the code gets by doing this. Even very stateful programs can be implemented this way, and the huge benefit is that you can follow the code linearly downwards. It’s also extremely easy to debug, as you can just step through each corutines `update()` function. +I'm hoping this simple example sort of shows how extensible and concise the code gets by doing this. Even very stateful programs can be implemented this way, and the huge benefit is that you can follow the code linearly downwards. It's also extremely easy to debug, as you can just step through each corutines `update()` function. ## When it gets bothersome What happens if we want to only act on changes to some of these variables? -``` +```c void update(coro_t* co) { bool has_pushed_lever1 = false; while (true) { @@ -113,39 +113,39 @@ You start having to do stuff like this. You might get the idea of using some event system just for this. Now it generally gets the job done, but as explained it gets hard to manage. Event systems play very poorly if you want situations like explained further above, where you have lots of things in your if statements. Having to set up event listeners for each one of those and somehow collecting them together is how you get horrible spaghetti code. -Callbacks also don’t allow you to change a corutine’s state in any meaningful way, which is even more of a problem. This means you will have to go back to switch statements again. How horrid! +Callbacks also don't allow you to change a corutine's state in any meaningful way, which is even more of a problem. This means you will have to go back to switch statements again. How horrid! You probably already know the solution to this. It is to just store the previous state of each such variable. -``` +```c typedef struct { bool state; bool last_state; } lever_t; ``` -If you have ever used or made a platform library that isn’t event based, you have probably encountered this for button presses or the like: +If you have ever used or made a platform library that isn't event based, you have probably encountered this for button presses or the like: `if (platform_key_pressed(KEYCODE_SPACE) == true)` -It’s essentially the same concept. Each frame, only one place edits the lever’s data, meaning that as long as every single piece of code is ran each frame, you can just check if the state has changed. +It's essentially the same concept. Each frame, only one place edits the lever's data, meaning that as long as every single piece of code is ran each frame, you can just check if the state has changed. ## How this is related to synchronous hardware? -When making synchronous hardware, you often have control signals which only exist for a single “clock-cycle”. +When making synchronous hardware, you often have control signals which only exist for a single "clock-cycle". Take this VHDL code for blinking an LED at 1Hz as an example -``` +```vhdl clock_enable_1hz_gen : enable_gen port map ( clk_50hz => CLOCK, resetn => NRESET, - clock_divisor => “00000001011111010111100001000000”, + clock_divisor => "00000001011111010111100001000000", enable => clock_enable_1hz ); alive_gen : process (CLOCK, NRESET) is begin if rising_edge(CLOCK) then - if NRESET = ‘0’ then - LEDR(0) <= ‘0’; - elsif clock_enable_1hz = ‘1’ then + if NRESET = '0' then + LEDR(0) <= '0'; + elsif clock_enable_1hz = '1' then LEDR(0) <= not LEDR(0); end if; end if; @@ -154,12 +154,12 @@ end process; This process is synchronous because it only does something during the rising edge of the clock signal. clock_enable_1hz is only on during one clock cycle, and is enabled every nth rising edge, such that we can toggle the LED at 1Hz. This technique is riddled across hardware design. When designing hardware, everything is ran each clock-cycle. Everything is an update() function. -This is essentially the same as our software, where we run each update function each “frame” instead of each “clock-cycle” +This is essentially the same as our software, where we run each update function each "frame" instead of each "clock-cycle" This constraint of hardware design, gives life to this unique way of sharing stateful information. And gives us a feature we can exploit when writing software as well. ## Using this knowledge -``` +```c bool secret_hold_button_click; void update(coro_t* co) { @@ -187,12 +187,12 @@ void update_special_room() { play_audio(&audio[AUDIO_SPECIAL_ROOM]); } ``` -We can propagate control signals through our “synchronous” codebase just by having variables that have a per-frame lifetime. +We can propagate control signals through our "synchronous" codebase just by having variables that have a per-frame lifetime. -I’ve found this extremely powerful. It’s simpler and more concise than an event system. It allows you to still use corutines, and is thereby easy to debug. It also creates manageable codepaths, and you can inspect the memory to see which states are active at any given moment. +I've found this extremely powerful. It's simpler and more concise than an event system. It allows you to still use corutines, and is thereby easy to debug. It also creates manageable codepaths, and you can inspect the memory to see which states are active at any given moment. This is not limited to booleans either. We can for example use counters. Or for more advanced info, you might want to to create an array instead. -``` +```c struct room_t { s32 changes_this_frame; // signed on purpose u32 people_in_room; @@ -213,7 +213,7 @@ void update_room(coro_t* co, room_t* room) { ## Comparison to callback driven system ### Simple condition -``` +```c void init() { event_add_listner(room.levers[0].on_click, try_open_room_secret); } @@ -226,7 +226,7 @@ ev_func(try_open_room_secret) { } ``` vs -``` +```c void update(coro_t* co) { while (true) { if (room.has_secret && lever_pressed(room.levers[0])) { @@ -238,11 +238,11 @@ void update(coro_t* co) { } ``` -As you can see, they’re fairly comparable currently. You could even say the event system has some benefits. +As you can see, they're fairly comparable currently. You could even say the event system has some benefits. ### More complex conditions *You need to press multiple buttons within a time frame.* -``` +```c timer_t* room_timer; bool lever_states[2]; @@ -282,7 +282,7 @@ ev_func(reset_levers) { } ``` vs -``` +```c void update(coro_t* co) { while (true) { bool lever_states[2] = {0}; @@ -306,12 +306,12 @@ lever_pressed: } } ``` -The event solution even requires us to create a new “timer” module. Meanwhile, corutines can naturally yield for a set amount of time just by checking what the current time is. +The event solution even requires us to create a new "timer" module. Meanwhile, corutines can naturally yield for a set amount of time just by checking what the current time is. -The event solution also has more management around it, requiring us to initialize the timer and event systems, as well as cleaning them up once we’re done with them. +The event solution also has more management around it, requiring us to initialize the timer and event systems, as well as cleaning them up once we're done with them. This only gets worse for the event driven solution once you add multiple types of events. You will have to pull your code into even more functions, and do checks from data accumulated from different event handlers. -The corutine solution doesn’t have to change—It is 100% extensible, we have yet to introduce any abstractions on it. +The corutine solution doesn't have to change—It is 100% extensible, we have yet to introduce any abstractions on it. ## Doing it the other way around @@ -346,11 +346,11 @@ void update_entity(entity_t* e) { } ``` -The above code makes it so you don’t have to track which entities are inside your circle, to disable immunity on their eventual exit. You also don’t have problems if the immunity fields are intersecting. +The above code makes it so you don't have to track which entities are inside your circle, to disable immunity on their eventual exit. You also don't have problems if the immunity fields are intersecting. The other way to achieve the same would be to loop over all immunity fields inside `update_entity()`. This would indeed work. However, it means you have to store all your immunity fields in some array(s). Toggling, enableing and doing weird things would get increasingly constrained. -The above code gives you full control over how the immunity fields are allocated and applied. You can have weird logic like only applying to entities on a team, or who are in the same building, or who are named in some certain way. All without changing the interface to towards the entities. It’s really flexible. +The above code gives you full control over how the immunity fields are allocated and applied. You can have weird logic like only applying to entities on a team, or who are in the same building, or who are named in some certain way. All without changing the interface to towards the entities. It's really flexible. <br> diff --git a/_posts/2024-05-08-VGA-Kontroller.md b/_posts/2024-05-08-VGA-Kontroller.md @@ -103,7 +103,7 @@ end; Så langt er det rett fram, la oss gå over til VHDL. ## Implementasjon i VHDL -``` +```vhdl vga_process : process (pixel_clock) is variable h_counter : integer range 0 to h_period - 1 := 0; variable v_counter : integer range 0 to v_period - 1 := 0; @@ -176,7 +176,7 @@ Figur 5. Skjerm koblet til DE2-115 kort. ![Figur 6](/assets/images/VGA/VGA_fig6.jpeg)\ Figur 6. Skjerm med figurer som endrer seg {: style="color:gray; font-size: 80%; text-align: center;"} -``` +```vhdl if ((x + pan / 1) / 32) mod 2 = 0 and ((y + pan / 1) / 32) mod 2 = 0 then vga_r <= (others => '1'); vga_g <= (others => '0'); @@ -191,7 +191,7 @@ end if; ## Tilkobling av RAM modul Første steget her er å flytte ut informasjon om x, y, og draw_time utenfor vga-modulen. Deretter skriver vi en enkel test for å se at det fortsatt fungerer -``` +```vhdl draw : process(CLOCK_25) is begin if rising_edge(CLOCK_25) then @@ -236,7 +236,7 @@ Dersom vi deler oppløsningen på 8, får vi da 80x60 * 24, som er 115200 bits. Det skal være godt innenfor. Vi kobler RAM modulen -``` +```vhdl vram_instance : vram port map( wrclock => CLOCK_50, wraddress => vram_wraddress, @@ -259,7 +259,7 @@ VGA_B <= vram_out(7 downto 0) when draw_time = '1' else (others => '0'); {: style="color:gray; font-size: 80%; text-align: center;"} process til testing av VRAM i VHDL, brukt sammen med signal tap logic analyzer. -``` +```vhdl fill_color : process(CLOCK_50) is variable i : integer range 0 to v_pixels * h_pixels / 8; variable slow : integer range 0 to 500000; @@ -295,7 +295,7 @@ begin end process; ``` Siden systemet er synkront, trenger vi å laste inn fargen en klokkesyklus før den vises. Dette kan gjøres med å utvide rekkevidden til x integer til å inkludere -1, deretter endre på hvordan tellerne oppfører seg når det er blanking time. -``` +```vhdl -- I VGA entity if h_counter < h_pixels then x <= h_counter; @@ -327,7 +327,7 @@ Figur 10. Sluttresultat av VRAM testing i VHDL, alle farger har blitt tegnet på ## Interface mot NIOS II Vi lager et enkelt bus interface -``` +```vhdl entity main is port( CLK : in std_logic; @@ -345,7 +345,7 @@ entity main is ); end entity main; ``` -``` +```vhdl bus_interface : process (CLK) is begin if rising_edge(CLK) then @@ -370,7 +370,7 @@ Figur 11. vga kontroller i platform designer Vi lager så programmer i C som rett og slett bare skriver til riktig adresse. Gradvis fylling: -``` +```c for (;;) { for (uint32_t c = 0; c < 3; c++) { for (uint32_t i = 0; i < 80*60; i++) { @@ -381,7 +381,7 @@ for (;;) { } ``` Gradient -``` +```c for (uint32_t y = 0; y < 60; y++) { uint32_t c = (y * 255/60); for (uint32_t x = 0; x < 80; x++) { @@ -396,7 +396,7 @@ for (uint32_t y = 0; y < 60; y++) { } ``` Bilde -``` +```c for (uint32_t i = 0; i < 80*60; i++) { uint32_t p = 0; p |= gimp_image.pixel_data[i*3+0] << 16; @@ -439,12 +439,12 @@ Figur 15. 4-bit og 3-bit fargepalett. <br> Font-atlaset lagrer vi i en gigantisk `std_logic_vector`, det er langt ifra optimalt, men det lar oss unngå timing problemer under lesing av atlaset. -``` +```vhdl signal bitmap : std_logic_vector(0 to 128*256-1); ``` Det første vi gjør er å utvide adresse-mappet, slik at vi gir brukeren mulighet til å endre på font-atlaset, samt mulighet til å velge om text-mode eller full-color skal brukes. -``` +```vhdl bus_interface : process (CLK) is variable curr_32_bit_addr : integer; begin @@ -486,7 +486,7 @@ Figur 16. Fontraption kjørende i DOSBox Dette lar oss eksportere mangfoldige text-mode skrifttyper til flere formater. For å automatisk generere en `std_logic_vector` i formatet vi selv ønsker, er vi nødt til å lage et lite skript som leser en bildefil og skriver ut masse tekst. -``` +```c int main() { for (int i = 0; i < 256; i++) { @@ -511,7 +511,7 @@ Figur 17. font i `std_logic_vector` Så ikke minst, prosessen for å skrive til skjermen ut ifra hvilken modus. Her ble en asynkron prosess valgt, siden ellers ville timing og klokkedomener blitt et problem. -``` +```vhdl mode : process (x, y, draw_time, vram_out, blink_white, bitmap, nreset, is_vga_text_mode) is variable current_character : std_logic_vector(0 to 127); variable code_point : integer range 0 to 255; @@ -570,7 +570,7 @@ end process; ``` `blink_white` blir laget av en ganske enkel prosess, lignende heart.vhd -``` +```c blink_white_gen : process(CLK) is begin if rising_edge(CLK) then @@ -585,7 +585,7 @@ blink_white <= blink_counter(24); ``` For å teste alle mulige kombinasjoner av farge, så tegner vi “A” på hele skjermen, også endrer vi forgrunn og bakgrunn ut fra x og y koordinat. -``` +```c for (uint32_t y = 0; y < 30; y++) { for (uint32_t x = 0; x < 80; x++) { int cfg = x * 16 / 80; @@ -604,7 +604,7 @@ VGA text-mode lar oss utnytte utrolig mye mer oppløsning selv om vi har begrens ## C driver implementasjon For å forenkle utviklingen av framtidig programvare lager vi en C-driver. Se datablad for mer informasjon om både driveren og for eksempel på bruk. -``` +```c #include <io.h> #include <system.h> #include <inttypes.h> @@ -678,7 +678,7 @@ void vga_text_mode_set_xy(int base_address, unsigned char x, unsigned char y, vg ![Figur 19](/assets/images/VGA/VGA_fig19.jpeg)\ Figur 19. Bilde av terminalen {: style="color:gray; font-size: 80%; text-align: center;"} -``` +```c void random_tm(const char* cb) { vga_set_mode(VGA_CONTROLLER_0_BASE, VGA_MODE_TEXT_MODE); diff --git a/_posts/2025-02-20-hello-world.md b/_posts/2025-02-20-hello-world.md @@ -10,7 +10,7 @@ Hello world! This day marks the creation of this website. (posts earlier than this one were made before the website was created) -``` +```c #include <stdio.h> int main() { diff --git a/_posts/2025-02-22-implications-of-OOP.md b/_posts/2025-02-22-implications-of-OOP.md @@ -15,7 +15,7 @@ Modern programming languages that "support" OOP **force** you to use their restr In the everyday sense, OOP is a software strategy where *functions* and *data* are tied together. I don't have an issue with that. If that's all that OOP did, then it would merely be an alternative syntax for accomplishing this: -``` +```c // not OOP (?) struct my_struct { int a, b, c; @@ -30,7 +30,7 @@ int calculate_sum(my_struct& self) { my_struct object = {1, 2, 3}; int res = calculate_sum(object); ``` -``` +```c // OOP class my_class { int a, b, c; @@ -54,7 +54,7 @@ Inheritance is the ability of a class to extend other classes. It allows you to add methods and members on top of a generic base class. It looks something like this. -``` +```c // OOP class my_base_class { int a, b; @@ -65,7 +65,7 @@ class my_class : my_base_class { } ``` But you can still do this without OOP. -``` +```c // not OOP (?) struct my_struct { int a, b; @@ -85,7 +85,7 @@ The "non-OOP" way is so far superior: ## So what's the catch OOP inheritance allows you to *override* functions. -``` +```c // OOP class my_base_class { virtual string to_string() { @@ -94,12 +94,12 @@ class my_base_class { } class my_class : my_base_class { string to_string() { // overridden function - return "my_class" + return "my_class"; } } ``` The "magic" part about OOP is that casting an object to its parent class retains the overridden methods in the parent class. -``` +```c void print_string(my_base_class obj) { print(obj.to_string()); } @@ -118,7 +118,7 @@ So RAII, *Resource acquisition is initialization*, that's the catch that makes O ### 1. Function pointers Function pointers manually do what the RAII and virtual methods do "automatically" for us. -``` +```c typedef string to_string_function_pointer_t(void* self); ////////////////////////////////////////////////// @@ -160,7 +160,7 @@ my_struct* make_my_struct() { } ``` Usage then remains the same. -``` +```c void print_string(my_base_struct base) { print(base.to_string()); } @@ -171,7 +171,7 @@ print_string(object.base); // prints "my_class" This example shows how much work the OOP language is doing for us. C makes it apparent how intricate of a solution this is. ### 2. Enums -``` +```c enum struct_type { TYPE_MY_BASE_STRUCT, TYPE_MY_STRUCT, @@ -184,13 +184,13 @@ struct my_base_struct { string to_string(my_base_struct* obj) { switch (obj.type) { case TYPE_MY_BASE_STRUCT: return "my_base_struct"; - case TYPE_MY_STRUCT: return "my_struct"; - default: return "invalid type"; + case TYPE_MY_STRUCT: return "my_struct"; + default: return "invalid type"; } } ``` -``` +```c void print_string(my_base_struct base) { print(to_string(base)); } @@ -205,9 +205,9 @@ print_string(object.base); // prints "my_class" As you saw, the function pointer strategy was pretty painful. However, it's very explicit. In the cases where we genuinely need a fully obfuscated API, this is perfect. Functions are a fundamental concept and are easy to reuse. The `to_string_my_struct()` can be used by any other function, which many OOP languages will not let you do. -``` +```c string to_string_my_struct(my_struct* obj) { - return to_string_my_base_struct(obj.base) + int_to_string(obj.c) + return to_string_my_base_struct(obj.base) + int_to_string(obj.c); } ``` \ @@ -224,7 +224,7 @@ The enum solution states its intent clearly, as it doesn't use advanced language You might have noticed I never created the `my_struct` in the enum example. This happened because we don't need multiple types anymore. The enums let us compose all our behavior in a single type! -``` +```c struct entity { entity_type type; struct { @@ -248,7 +248,7 @@ In extreme cases, multiple files with tens of classes and functions become a sin Additionally, if you're concerned about the memory usage of having one large struct, you can use a "tagged union". ### 3. You can combine the two above -``` +```c struct entity { entity_type type; struct { diff --git a/_posts/2025-02-22-making-generic-data-structures-in-C.md b/_posts/2025-02-22-making-generic-data-structures-in-C.md @@ -21,7 +21,7 @@ Doing this creates friction, and it has no type safety. Example of a `void*` based linked list: -``` +```c typedef struct generic_linked_list generic_linked_list; struct generic_linked_list { generic_linked_list* next; @@ -46,7 +46,7 @@ void llist_stack_push(generic_linked_list** first_node, void *data, size_t data_ ## The simple way Instead of plastering `void*` everywhere, create macros that assume member names. -``` +```c #define llist_queue_push(f,l,n) ((f)==0) ? (f)=(l)=(n) : ((l)->next=(n), (l)=(n), (n)->next=0) #define llist_queue_pop(f,l) ((f)==(l)) ? ((f)=(l)=0) : ((f)=(f)->next) @@ -55,7 +55,7 @@ Instead of plastering `void*` everywhere, create macros that assume member names ``` Having generic *operations* encourages defining the structures yourself. -``` +```c typedef struct llist_int llist_int; struct llist_int { llist_int* next; @@ -64,7 +64,7 @@ struct llist_int { ``` Then you use it like below. -``` +```c llist_int* list_first; llist_int* n = malloc((sizeof(*n))); @@ -77,7 +77,7 @@ It's a lot simpler. It's type safe and gives the user control of memory manageme Meanwhile, with the `void*` method, merely reading the list requires typing `*(int*)list_first->data`. Retaining control over data types also makes it straightforward to compose large structures. -``` +```c typedef struct tree_node tree_node; struct tree_node { tree_node* parent; @@ -96,7 +96,7 @@ We can use this structure as is. We don't need the boilerplate that comes with a ## The "hide away" method You can also conceal the header information and provide the type in a friendlier way. -``` +```c typedef struct { int sz; } _dyn_arr_header; @@ -109,7 +109,7 @@ typedef struct { arr[dyn_arr_get_sz(arr)-1] = new_val \ ) ``` -``` +```c dyn_arr(float) my_arr = NULL; dyn_arr_push(my_arr, 10.0f); @@ -125,7 +125,7 @@ This approach creates more obfuscated and harder-to-read code than exposing type Although using generic operations rather than types is the takeaway from this post, I figured I should demonstrate an extreme example. Let's make a hash table. To ease use, we can provide macros that generate the correct structures. -``` +```c #define htable_def_ex(_htable_entry_T) \ struct { \ u64 table_sz; \ @@ -136,7 +136,7 @@ To ease use, we can provide macros that generate the correct structures. #define htable_def(_T) htable_def_ex(struct {u64 hash; void* next; _T val;}) ``` We wrap our operations in generic macros like before. This time, using internal functions and processing the input and output of them. -``` +```c typedef struct {u64 hash; void* next;} null_htable_entry_t; typedef htable_def_ex(null_htable_entry_t) _null_htable_t; @@ -178,7 +178,7 @@ void* _htable_find_or_create(void* htable, size_t entry_sz, uint64_t key) ``` Usage code: -``` +```c htable_def(int) int_htable; int_htable id_table = { .table_sz = 1001, @@ -198,7 +198,7 @@ int_htable id_table = { ``` If you wish to avoid using `typeof`, you can make another struct member to use as the "return" value. Like this: -``` +```c #define htable_def_ex(_htable_entry_T) \ struct { \ u64 table_sz; \ diff --git a/_posts/2025-02-23-my-old-projects.md b/_posts/2025-02-23-my-old-projects.md @@ -223,7 +223,7 @@ ArduinoNative is probably the most usable of my projects. The code isn't that gr Compared to other ways of simulating an Arduino, using ArduinoNative is very simple. Serial and AnalogRead Example -``` +```c #define AN_BOARD_NANO #define AN_IMPL #include "ArduinoNative.hpp" @@ -312,7 +312,7 @@ The implementation additionally came with an optional packed free-list. I don't use this project anymore. I would rather use a linked list or static allocation. -``` +```c gs_bucket_array(float) ba = gs_bucket_array_new(float, 100); // Bucket array with internal 'float' data, where each bucket is 100 floats gs_bucket_array_reserve(ba, 2); // Reserves 2 buckets gs_bucket_array_push(ba, 3.145f); // Pushes 3.145 onto the end of the bucket array diff --git a/_sass/_main.scss b/_sass/_main.scss @@ -6,6 +6,17 @@ e,:after,:before { padding:0; } +/*********** fonts ************/ + +@font-face { + font-family: "Iosevka"; + src: url("/assets/fonts/Iosevka-Etoile.ttf"); +} + +/*********** syntax highlighting ************/ + +@import "/assets/css/vs.css"; + /*********** body ************/ html { @@ -22,7 +33,6 @@ body { font-family: noto-serfi, serif; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; - line-height:1.5; color: rgb(22, 23, 26); background-color: #faf9ee; } @@ -127,7 +137,6 @@ a { } h1,h2,h3,h4,h5 { - line-height: 1; margin: 1rem 0; font-weight: bold; } @@ -152,10 +161,12 @@ code, pre { pre code { border: none; + background: transparent; } code { - padding: .1rem; + padding: 0px; + font-family: "Iosevka"; } pre { diff --git a/_todo.md b/_todo.md @@ -1,16 +1,21 @@ # TODO ## Blog ideas: + +### electronics - Chicken door -- Ghostbusters Costume -- Camping -- Tips for programmers - Learning tips for electronics -- How to organize software projects (Folders are magic) - modelsim FLI tutorial (VHDL) - vhdl SPI tutorial - dealing with high power noise (basically om coil kontroller) +### other +- Camping + +### programming +- Tips for programmers +- How to organize software projects (Folders are magic) + - Productivity through simpler software Only one monitor, previously had more I use DWM, workspaces. diff --git a/assets/css/vs.css b/assets/css/vs.css @@ -0,0 +1,32 @@ +.highlight .hll { background-color: #ffffcc } +.highlight .c { color: #AC4426 } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #3B6EA8 } /* Keyword */ +.highlight .cm { color: #AC4426 } /* Comment.Multiline */ +.highlight .cp { color: #AC4426 } /* Comment.Preproc */ +.highlight .c1 { color: #AC4426 } /* Comment.Single */ +.highlight .cs { color: #AC4426 } /* Comment.Special */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gh { font-weight: bold } /* Generic.Heading */ +.highlight .gp { font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { font-weight: bold } /* Generic.Subheading */ +.highlight .mi { color: #97365B } /* Literal.Integer */ +.highlight .kc { color: #97365B } /* Keyword.Constant */ +.highlight .kd { color: #3B6EA8 } /* Keyword.Declaration */ +.highlight .kn { color: #3B6EA8 } /* Keyword.Namespace */ +.highlight .kp { color: #3B6EA8 } /* Keyword.Pseudo */ +.highlight .kr { color: #3B6EA8 } /* Keyword.Reserved */ +.highlight .s { color: #4F894C } /* Literal.String */ +.highlight .ow { color: #3B6EA8 } /* Operator.Word */ +.highlight .sb { color: #4F894C } /* Literal.String.Backtick */ +.highlight .sc { color: #4F894C } /* Literal.String.Char */ +.highlight .sd { color: #4F894C } /* Literal.String.Doc */ +.highlight .s2 { color: #4F894C } /* Literal.String.Double */ +.highlight .se { color: #4F894C } /* Literal.String.Escape */ +.highlight .sh { color: #4F894C } /* Literal.String.Heredoc */ +.highlight .si { color: #4F894C } /* Literal.String.Interpol */ +.highlight .sx { color: #4F894C } /* Literal.String.Other */ +.highlight .sr { color: #4F894C } /* Literal.String.Regex */ +.highlight .s1 { color: #4F894C } /* Literal.String.Single */ +.highlight .ss { color: #4F894C } /* Literal.String.Symbol */ diff --git a/assets/fonts/Iosevka-Etoile.ttf b/assets/fonts/Iosevka-Etoile.ttf Binary files differ.