←back to thread

221 points benbridle | 3 comments | | HN request time: 0.863s | source

Hey everyone, this is my latest project.

Bedrock is a lightweight program runtime: programs assemble down to a few kilobytes of bytecode that can run on any computer, console, or handheld. The runtime is tiny, it can be implemented from scratch in a few hours, and the I/O devices for accessing the keyboard, screen, networking, etc. can be added on as needed.

I designed Bedrock to make it easier to maintain programs as a solo developer. It's deeply inspired by Uxn and PICO-8, but it makes significant departures from Uxn to provide more capabilities to programs and to be easier to implement.

Let me know if you try it out or have any questions.

1. kragen ◴[] No.44568273[source]
I like things like this.

One of the big differences from Uxn is the introduction of undefined behavior; by design, you can break it, unlike Stanislav's legos. So presumably Bedrock programs, like C programs, will do different things on different implementations of the system. That's not fatal to portability, obviously, just extra write-once-debug-everywhere work.

replies(1): >>44577076 #
2. benbridle ◴[] No.44577076[source]
The undefined behaviour is limited to only a couple of very edge-case situations that would cause issues for the program anyway, like overflowing the stacks. My thoughts were that a program would have to be broken in order to have triggered one of these situations in the first place.
replies(1): >>44578024 #
3. kragen ◴[] No.44578024[source]
All programs are broken. No program over 100 lines is bug-free, probably. Maybe seL4, but even seL4 had to be patched for Spectre.

In particular, you can be sure that if people build implementations that don't detect those situations, people who test their code on those implementations will ship code that triggers them. It's pretty easy to underflow a stack at startup if you don't use whatever you popped off of it for anything important, unless the thing you happen to be overwriting is important and/or gets written to later by another means. Limited stack overflow is less common but does occasionally happen.

What typically happens in situations like this is that new implementations have to copy the particular handling of supposedly undefined behavior that the most popular implementation of the platform happened to have. But because it isn't documented, or maybe even intentional, they have to reverse engineer it. It's often much more complex than anything that anyone would have come up with on purpose. This all strikes me as a massive waste of time, but maybe it's an acceptable tradeoff for squeezing that last 30% of performance out of your hardware, a consideration that isn't relevant here.

In the days before Valgrind we would often find new array bounds errors when we ported a C or C++ program to a new platform, and Tony Hoare tells us this sort of thing has been ubiquitous since the 60s. It's hard to avoid. Endianness used to be a pitfall, too, and Valgrind can't detect that, but then all the big-endian architectures died out. The C standards documents and good books were always clear on how to avoid the problems, but often people didn't understand, so they just did whatever seemed to work.

If you want write-once-run-anywhere instead of write-once-debug-everywhere you have to get rid of undefined behavior. That's why Uxn doesn't have any.