According to Wikipedia, the code of the game is written 99% in assembly, too bad the article doesn't mention that. Maybe they decided that is not a valuable information for their readership but I guess, here, lots of people will be as impressed by that.
I believe Chris Sawyer was also responsible for the PC port of Frontier Elite II (from the Amiga original by David Braben), and he put an advert in some of the spaceports, along the lines of “Coming soon: Chris Sawyer’s Transport Game”.
One assumes he was an expert in 68k and x86 assembly.
That fact comes up in every single rollercoaster tycoon discussion, so maybe they just assumed most knew about it. It also doesn't seem relevant to the point of the article.
He used a relatively high level macroassembler. It's not HLA, but it could do things like dereference struct fields and reference various (global?) variables. See here, for example: https://lifeandtimes.games/episodes/files/28
Honestly, it's not that hard. Not for a decent assembler.
The "hard parts" of assembly are things like trying to do multi-precision stuff on smaller precision machines (like 16- 32- bit math on an 8-bit processor). Just coming up with the tiny algorithms we all take for granted. Of course, today, you don't have to invent those.
Folks think that written in assembly is all bit twiddling and high, dense data structures. But that's not necessarily true.
On a modern x86, it's not that much different from normal C in terms of data sizes and data structures. 16- and 32-bit registers. Add/Sub/Mult/Divide instructions. Block move instructions. Modern assemblers handle structured data. Stupid assemblers handle offsets readily. Assemblers handle local and private symbols, exporting routines, code modularity. You don't have to worry about addresses and what not, linkers do all of that. Symbolic debuggers like anything else. Not like they have to burn a PROM and shove it into a board every dev cycle.
But, most important, once you've got started, once you have the common elements of programming (parameter passing, looping, math, structures, pointer dereferencing), I mean, that's it, right? Rinse and repeat. That's what higher level languages give you "for free". Not every line is hand crafted, hand optimized, etc. Much of it can be stomped out with macros for anything that takes more than a few instructions. Tada -- you now have a higher level language (for small values of "higher").
If anything it's just more tedious. Less code density (assuming little macro use) per screen, etc. But even then, once you have your momentum, once you have your routine patterns, they just fall out of the code as you read it.
I remember my assembly class in college (I was not a good college student). I stopped going to class because the teacher kept asking me system questions. She gave me an incomplete, and I had to do all the projects again next term (that was, honestly, quite nice of her -- she could have just failed me).
So, I did those projects. Those simple Assembly class projects. Trivial little programs "Convert decimal to binary" type stuff.
What I did was I wrote a large macro library, inspired by Forth. I basically ported a rough Forth vocabulary to assembler macros. That "convert decimal to binary" project?
; Convert 10 to Binary and print it
PUSH 10; PUSH 2; PUSH BASE; STORE; PUSH BUFFER; NUMBER; PUSH BUFFER; PRINT
So I turned in my projects, each one was like that -- 10, 20 "instructions". I said "Here's my projects", handing 10, small, 2-3 page printouts, "but you'll need this to understand them" and I gave her a 3/4" thick printout of the macro library.
Yea, I was kind of a jerk. Just where my head was at.
Figured I'd get an F or and A. She gave me an A. (I mean, if nothing else, I did demonstrate a solid understanding of assembly language programming!)
But the underlying point is that it's just patterns, patterns we get used to using and writing. It can be inscrutable to the uninitiated, but the curve, particularly on modern processors, is not that steep. Like I said, you're not just shifting and adding to multiply anymore like the old days. And staring at a disassembly of raw code is not the same thing as writing assembly language with modern assemblers.
>What I did was I wrote a large macro library, inspired by Forth.
In an elementary-school BASIC class, I completed an assignment using `READ`/`DATA` when it was not necessary to do so. For a later assignment involving writing a quiz game, I resubmitted the same code with the `DATA` lines appropriately modified!
Obligatory reminder that wikipedia is NOT (trying to be) a source. This particular fact comes from Chris Sawyer's own website, that has some more interesting details as well: https://www.chrissawyergames.com/faq3.htm
You can easily find sources from Wikipedia by following the numbers in brackets (here "[3]").
Having worked with people who wrote games like the original Driver - large chunks of code used to be re-written by hand in assembly for performance late in development, or the game just didn't run fast enough. 90s compilers just weren't that good. I think in 2024 if you think you can do better than the compiler you're mostly fooling yourself, but 25 years ago that wasn't the case.
Even then though C has always had trivial to use and very tightly integrated inline ASM that you could use at any time and had been a thing since inception all those years ago for this very reason.
oh yeah I mean the source code for Driver has tonnes of inline assembly inside C files, so there was definitely value in using it. Writing the entire game in assembly is just taking it a step further, and like someone else said - if that's what you're most comfortable with, I don't think that's as insane as it sounds.
A 166 MHz Pentium is massive overkill for RCT. Consider that just a 100 MHz Pentium could render Quake locked at 60 fps, and a 166 MHz Pentium will have been a later and much improved version which also had MMX. The 233 MHz Pentium with MMX ran the original Unreal with coloured lighting and everything.
BTW I also remember the cache sizes of all these chips and wrote mostly in asm on them. Most people who were good at asm coding didn't stop because it took too long (you can always focus on just the hot loops), we stopped because we started getting our asses handed to us by C/C++ compilers.
Finally, just because asm coding provided sufficient performance, doesn't mean it was necessary, and it's of course possible to write arbitrarily slow asm code too (like a bubble sort in asm vs quicksort in C).
Chris Sawyer knew how to crank out assembly code. He'd been doing it for several games, so maybe it really was the best way for him to develop this rather than futz about with C++ or whatever.
From reading the OpenRCT2 code base it was C-style with PUSHA features. You basically need to create a bunch of wrappers over function calls, to construct a sort of domain specific language/macro collection for your specific application.
I can respect the dude but in many ways RCT was a continuation of his previous games.
As people who study software development we have to sometimes call it like it is.
Using ASM was not the right choice as it didn't provide any obvious advantages compared to languages like C.
The genius of OpenRCT2 wasn't in the language choice but rather in the immersive world available in your cereal box.
There is no right language choice for a game, same how there's no right choice to live a life. There are advantages and disadvantages, and in retrospect, we can see how things turn out.
There are language choices in projects where the choice seems to hurt the project, that's for sure. Wrt/ RCT, I don't see a single downside though. The cases I'm thinking about are Minecraft, for example, with its horribly performing Java code. Cities Skylines with their engine choice and usage, resulting in 30-40 fps even on high end hardware. Project Zomboid with LUA - many performance problems, and artificial limitations to keep performance to a reasonable limit.
Also consider that often, a project either gets off the ground with the sub-optimal choices, or it doesn't, at all. And as we see from the results, a sub-optimal, but fun game is better than an optimal, but non-existing game. At least if you consider fun and success desirable.
It was the right choice for the developer because he had years of experience developing other games, he probably had a structure and a good enough library by the time he started working on RCT.