5 ms·
Very refreshing to see this. It is so much fucking easy to grok bare metal C compared to the <flavour of the year>-script junk that floats around these days.
by subhro 5y ago
Very refreshing to see this. It is so much fucking easy to grok bare metal C compared to the <flavour of the year>-script junk that floats around these days.
- kennywinker 5y agoAh yes, nice easy to grok code like `curval &= ~(field_mask << shift);` :P But for real - I’ve had way more luck grokking embedded rust than all of the bare metal C examples i’ve looked at. C breeds dense bittwiddling and code that relies on inscrutable compiler behavior. There are easier ways to learn how these systems work at a bare-metal level.
- PaulDavisThe1st 5y agoWould you like to propose or reference a way of doing bit-twiddling that is clearer than this? Also hint: C doesn't breed bit-twiddling, writing software that actually interacts directly with hardware does.
- kennywinker 5y agoThat was just a throwaway example of a pretty write-only line of code from the op codebase, but since you asked: One operation per line. A comment for every operation. Shifts that explicitly say if they are wrapping or overflowing. Rust uses ! instead of ~ but if I had my way it’d be a named function like bitwise_invert(). // curval &= ~(field_mask << shift); // original line // pseudo-rust version let shifted_mask = FIELD_MASK.wrapping_shl(shift); // be clear about what kind of shift we’re doing let invered_mask = shifted_mask.bitwise_invert(); // use a fictional invert fn to avoid single-char operators. let shifted_val = curval & inverted_mask; // new variable instead of mutating the existing one Ideally those comments would say WHY we’re doing those ops rather than what’s notable about them - but i didn’t dig into the code enough to write explanations. And then we let the compiler crush that into an efficient lil one liner like the author of the original code did manually.
- junon 5y agoNo thanks, I'll take the C version any day.
- kennywinker 5y agoSure, the single line is more aesthetically pleasing. Compact, clever, concise. But try fixing a bug or adding new functionality to that one line. Especially as a beginner. This is supposed to be an educational codebase.
- isometimes 5y agoI've stated in part1 of the tutorial that "This tutorial is not intended to teach you how to code in assembly language or C". My goal was to demonstrate some basic principles to get code running on bare metal, encourage curiosity, further my own knowledge and document my findings. I appreciate that more self-documenting code might be desirable, but to some people (me included) a large number of lines can be as off-putting as more esoteric syntax. I acknowledge, however, that it is very hard to please everyone!
- kennywinker 5y agoHi! Thank you for writing and publishing this project! Just to clarify: no part of my critique was aimed at you or your choices in this codebase. My main point is that unlike the original commenter in this thread, i believe that well written C is not as clear and simple as well written rust (or other modern languages). I then tried to back that up by cherry picking a random line of “c-like” code from your codebase. My beef is with C, not you or anybody else using it :)
- isometimes 5y agoThat makes complete sense, and I don’t take it personally. It’s been so cool to read all this valuable feedback! I must say that I’m very comfortable in C, only because it’s where I landed up as a kid. I actually find it way less confusing than more “modern” languages (I kinda skipped OO etc.!), and I enjoy the “control” it gives. Maybe you can’t teach an old dog new tricks after all! ;-)
- Veserv 5y agoThey are just implementing a generic contiguous bitfield clear. field_mask was probably constructed as ((1 << width) - 1) instead of as a manifest constant. So you can just do: ClearBitField(input, width, shift) { return input & ~(((1 << width) - 1) << shift) } Now you just use that everywhere you would clear a contiguous bitfield which is a pretty common operation when operating on hardware. Now all your bit-twiddling is isolated to a single well-defined generically useful function instead of repeating it a billion times. We know this is a generically valuable operation since this is basically a C implementation of the ARMv8 bfi (b)it(f)ield (i)nsert instruction with a fixed 0 argument or in assembly: BFI X{n}, XZR, #shift, #width
- kennywinker 5y agoI like this answer too. When opaque code is irreducibly opaque, put it in a fn with a well chosen name.