Langslowlevel

Low Level #

For the sake of avoiding arguments about if a language is low or high level, my criteria is that it can be reasonably used to write code for micro controllers.

In the intro to programming chapters we looked at some of the differences between low level and high level languages, and how at the very bottom of the layers of abstraction there’s assembly, so let’s start there.

Assembly #

1
2
3
4
5
6
7
8
9
.LC0:
  .ascii "hello world!\015\000"
main:
  push {r3, lr}
  movw r0, #:lower16:.LC0
  movt r0, #:upper16:.LC0
  bl puts
  movs r0, #0
  pop {r3, pc}

Assembly code is specific to an Instruction Set Architecture or ISA, that means it is written for a specific family of processors. Most laptops and desktops today run on x86_64 processors made by either AMD or Intel, while most tablets and phones use processors that use ARM. Of course, there are many, many other instruction sets than these two. For example, 6502 assembly is particularly easy to write, as it’s from an era Popular video game consoles and computers, such as the Atari 2600, Atari 8-bit family, Apple II, Nintendo Entertainment System, Commodore 64, Atari Lynx, BBC Micro and others, use the 6502 or variations of the basic design.
-from that same wikipedia article
when programming at the assembly level was still common. On the other hand, RISC-V is a newer ISA that has been gaining a lot of traction due to its open nature.

Knowing ASM can allow you to do some black magic, like pushing out FizzBuzz at 57.2GiB/s or may even be required to mundane tasks on micro controllers.

There’s only really a handful of instruction sets you’re likely to encounter today though, and of them the two most common by far are x86_64 and ARM. x86_64 (often, incorrectly, called just x86 - though I’ll do that often here as well) is very, very annoying to read because it is a CISC (Complex Instruction Set Computer) design. Complex is no joke, while the most complex variant of ARM has ~60 instructions (many of which won’t be available on all processors), x86_64 has (due to a myriad of extensions onto the instruction set) has roughly 1,000 depending on how you count them.

This could derail into a whole conversation about ISA, but you should really go read [TODO] for that.

x86 Assembly: Hello World! (John Hammond)

Say hello to x86_64 Assembly 1-8 by 0xAX

Let’s Learn x86-64 Assembly! Part 0 - Setup and First Steps

Part 1 - Metaprogramming in Flat Assembler

Part 2 - We’re Writing a Virtual Machine

Linux-kernal-module-cheat [GitHub] has a nice guide to ASM

‘Furby’ Source Code is in 6502 assembly

[TODO] - a lot, talk about the varieties and differences of C and C++ and C# and why thery’re good and bad, etc.

C #

1
2
3
4
5
#include <stdio.h>

int main(void){
    printf("Hello World!\r\n");
}

The next stop is the language that simultaneously gets the “most used by embedded” and “most hated for embedded” awards.

You’ve already seen C if you followed along with the intro to programming chapters, and really, by following those you’ve seen most of what it has to offer. It’s a pretty minimalist language with a small standard library. It’s both beautiful and immensely frustrating in its simplicity, mostly because of just how much it lacks in terms of safety, code organization systems, and portability.

You see, for as much as everyone likes to shout from the rooftops that C supports any and all processors ever made, they’re only half telling the truth. The problem is, C isn’t a programming language anymore (Aria Beingessner).

As that article argues, it’s turned into more of a “lingua franca of programming” but I’d also argue it’s not even one language anymore, because each compiler (GCC, Clang, MSVC, TCC, IAR, …) all support different prepossessing macros and language revisions, be it ANSI C, C89, C99, C11, or C23, witch each compiler and C version having a non insignificant amount of compatibility fusterclucks between one another.

Still, it’s a language that pretty much every programmer should know and know well and setting the above aside, it does still port to most platforms with lower effort than anything else.

So you really should dive into it. Learn pointers, macros, include guards, bounds safe function variants, memory alignment issues, asm(), and all. It’ll make you a better programmer.

Just, if you’re going to write in C, at least unit test your code. If you do that - if you care - C can be the safest option.

If you’re really looking to make C easier to work with - albeit in a way that’ll likely annoy others working on the code - Cog may come in handy.

If you’re looking for a good reference guide to come back to when needed, bookmark Beej’s Guide to C Programming.

C++ #

1
2
3
4
5
6
#include <iostream>

int main() {
    std::cout << "Hello World!";
    return 0;
}

If you want to increment C by one, you’d write C++.

C++ has a reputation for being overly complicated, throwing in everything and the kitchen sink worse than Python does, changing every 5 years, and being a little too flexible.

It has absolutely earned all of those criticisms. It’s also one of the most commonly used programming languages exactly because of all of those things. If you like some of C’s close-to-the-metal nature, but want a bit more functionality to be included so you’re not always reinventing the wheel or fighting things to have some semblance of type safety, C++ is probably a good bet.

Just know that while it’s possible to become a good C programmer than can read any C code and know what it’s doing (outside of intentionally obfuscated code), C++ is nearly a different language to each project which uses it. It’s object oriented, it’s heavier, and it still lets you shoot yourself in the foot, but now with a rube Goldberg machine filled with gunpowder instead of a pistol loaded with uninitialized memory.

If C is a desert, C++ is the Amazon. Both can be deadly and beautiful in their own ways.

If you’re looking to learn C++, https://www.learncpp.com is a decent bet. https://arobenko.github.io/bare_metal_cpp/ may prove helpful too.

Zig #

1
2
3
4
5
const print = @import("std").debug.print;

pub fn main() void {
    print("Hello, world!\n", .{});
}

A general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.

https://ziglang.org/

If Rust is the next logical progression for the majority of C++ programmers, Zig is the future for C programmers. We need unsafe code to be productive, so we have it. We need good ways to write compile time code for our targets with limited memory, and so we have it. Zig fits very nicey as an upgrade to C without throwing everything and the kitchen sink in at the same time.

For a proficient C programmer, the bullet points on the homepage should say everything you need to hear. Seriously, go read it: https://ziglang.org/

I highly recommend checking out each of these:

Additionally,

are interesting reads.

Zig is also particularly nice to develop in, thanks to the Zig Language Server. If you try Zig, make sure to set it up.

Rust #

1
2
3
fn main() {
    println!("Hello, world!");
}

Rust’s rich type system and ownership model guarantee memory-safety and thread-safety — enabling you to eliminate many classes of bugs at compile-time.

Basically, Rust doesn’t let you screw up the №1 thing that causes bugs.

It enforces writing code that’s safe (except when you specify you want to do something unsafe). It has a lot of other really nice features and makes using efficient data structures easy.

While I think Rust is great for desktop programs, for embedded, When Zig is safer and faster than Rust from ZackOverflow makes a good argument for why you may want to oxidize everything - especially if you’re working on embedded hardware.

If you are working on desktop software though, I can not stress strong enough how much better of a choice Rust is than C or C++ for the vast majority of programs now.

If you’d like to learn Rust, I recommend A half-hour to learn Rust as well as

Rust isn’t a “new” language anymore, but it’s certainly not as battle tested as C or C++, so to show the maturity, just check out https://www.redox-os.org/, a full OS written in Rust!

Also, it’s worth noting Rust can be used to generate Web Assembly (more about that below). This means you can (in a roundabout way) use it on the Web. This isn’t unique to Rust by any means, but Rust is heavily used for it and has good resources for doing so.

You may also want to check out CXX for C++ ⟷ Rust interop. If you’re looking for some fun projects to get started in Rust, you may want to use Nannou, a creative coding framework for Rust.

LLVM IR #

https://llvm.org/docs/LangRef.html

Nim #

Note, I don’t love calling Nim low level, but by my definition of “runs on a uC, it is: https://github.com/mwbrown/nim_stm32f3 This feels sorta gross, since Odin isn’t, but here we are.

1
echo("hello world")

Nim is a statically typed compiled systems programming language. It combines successful concepts from mature languages like Python, Ada and Modula.

https://nim-lang.org/

Further, Nim allows for easy ‘metaprogramming’ which basically means you can [TODO]

Nim’s syntax is python-like-ish

MicroPython/CircuitPython #


If you would like to support my development of OpGuides, please consider supporting me on GitHub Sponsors or dropping me some spare change on Venmo @vegadeftwing - every little bit helps ❤️