This weekly debrief is for paying supporters of my work. Please only read if you’ve paid. Thanks!
→ Click here if you've paid ←
These past few weeks I’ve been making the most of the hot weather, spending time with family, working through ideas for future projects, and studying for university.
Peak
Peak is a planned high-level dynamic language for the Bedrock computer system.
The biggest issue I can see so far for Bedrock is that the barrier to entry for complete beginners is too high, the amount of knowledge that you need in order to go all the way from looking at an empty text file to seeing a picture moving across the screen is enormous. I want Bedrock to be a viable introduction for people who don’t have the slightest idea how to program a computer, and to do that we need a high level language that makes it possible to create something visual in just one line of code. Without that visual feedback, there’s nothing to encourage beginners to dig in and learn more.
My idea of success for Bedrock is when people want to use it as the sole computer system for an 8-hour game jam; programming, making art and music, and exporting a playable game at the end. Part of this will just be getting around to writing high-quality programs for making all kinds of game assets, but the rest of it comes down to having a programming language that can be used to throw together a quick program. People don’t want to be pushing, popping, and balancing stacks when they could be drawing up character art or slotting together a user interface. There’s that old epigram from Perlin, “a programming language is low level when its programs require attention to the irrelevant”.
I’ve always planned for there to be three official programming languages for Bedrock:
- Bedrock assembler
This is the default language for Bedrock, and is included in the project specification. The design is bare-bones: it’s small enough to hold the whole thing in your head, while still being flexible enough to write higher-level programs. It isn’t too difficult a task to rewrite the entire assembler from scratch (important for the longevity of the language), and it’s the language of choice if you want to grit your way through low-level programming and squeeze every last drop of performance out of a machine — but most people don’t want to do that. You can think of Bedrock assembler as being like a bicycle, simple and swift. - Torque assembler
Torque offers the premier experience for low-level programming, whether for Bedrock or something more esoteric. If you want code that maps directly to Bedrock bytecode, but you find the Bedrock assembler too limiting, Torque is the language for you. Compile-time expressions, arbitrary string encodings, recursive macros that accept values and code blocks as arguments; it’s great for experienced programmers, but terrible for beginners. Think of Torque as a bicycle welded to a blistering hot jet engine. - Peak
Peak is the language for when you want to get things done. High-level, traditional syntax, functions and lists and dictionaries. You’ll be able to call a standard function to draw something to the screen, instead of jamming bytes directly into raw device ports. To complete our list of metaphors, think of Peak as a car: there are only a handful of controls, it can take you much further, but it’s a mess of machinery under the hood.
See the Peak project page if you’re interested in my ideas for the language so far.
I’m heavily inspired by the syntax and simplicity of Lua (and, by extension, Lil), and by the kinds of things that people have been able to make with PICO-8. With a syntax similar to Lua, people will be able to take what they’ve learned from Peak and apply it to more mainstream programming languages, like JavaScript or Python.
The biggest difference from all of those systems is that I also want Peak to be an on-ramp to learning about Bedrock. You’ll use Peak when you’re first learning to write programs for Bedrock, and you can get confident with writing more and more complex programs in Peak, but one day you might find that some parts of your program are just too slow for what you’re trying to accomplish, and you need more power. When you reach this point, you’ll be able to dip into Bedrock assembler, typing a fragment of assembler code directly inside your Peak program and calling it just like a regular function. All that we need in order to pull this off is a native Bedrock assembler and a relocatable module format for linking assembled bytecode into a program at runtime.
A relocatable module format for Bedrock
Unlike with Uxn (the system Bedrock was inspired by), the Bedrock instruction set doesn’t include any instructions for performing relative jumps, so all jumps in a program must be to absolute addresses. This means programs can’t be loaded just anywhere; they have to be loaded to a specific memory address in order for the jumps to line up with their targets.
This is normally fine: we only ever run one program at a time, so we load it at the start of memory and everything is swell. But what if we want to load code at runtime, extending an existing program with more features?
For this, we’ll need a new format for storing these relocatable modules. Addresses in Bedrock are pairs of bytes, so if we calculate addresses as usual — with the first byte of the program being address zero — then all we need to do when loading the code is to add the real start address to each address value. If the module was loaded in at address 300, then we add 300 to each address in the module. To keep track of the addresses in the assembled module, we just need to tack on a table that points to each of these addresses, so that we can find the location of each address by iterating over the table.
Fixing Torque
I received a kind email just before the new year, pointing to an issue in the version 3 release of Torque. This release included a huge rewrite of most of the internals of the assembler, and while rewriting everything I’d accidentally messed up the addresses calculated when a block value is passed to a macro invocation.
The core of the issue was that any value passed to a macro is eagerly evaluated, which didn’t seem like such an issue at the time. Expressions are crunched down to integers or lists, blocks are crunched down to sequences of words, everything is so much easier to work with once it’s been evaluated. But, crucially, every time a word in a block was assembled, the global address counter was getting incremented, even if that word hadn’t yet been jammed into the final program. This was done so that the addresses of any label definitions floating around inside the block would be calculated correctly, but it also meant that even if the block argument wasn’t actually used inside the macro (or if it was used more than once) then the address counter would still only be incremented as if exactly one copy of the block was included in the program, so the address values would all be wrong.
Anyway, the solution was to defer the evaluation of block values until they’re actually used by the macro, and to re-evaluate them each time they’re used. Everything works great now, the fix was released the next day with version 3.0.1.
Studying for uni
I don’t have much to say here. I’m hoping to get into an engineering course at university this year, so I’ve been relearning high school maths and physics in preparation. I’ve written up so many notes on the Mathematics page.
Thanks
That’s enough writing for this week. Thanks to everyone for supporting my work, it’s a lot of fun to work on and think about. Have a good one!