My reading is that there aren't really a lot of addressing modes on 286, as there are on 68000 and friends, rather every address is generated by summing an optional immediate 8 or 16 bit value and from zero to two registers. There aren't modes where you do one memory fetch, then use that as the base address for a second fetch, which is arguably a vaguely RISC flavored choice. There is a one cycle penalty for summing 3 elements ("based indexed mode").
What you say about memory indirect addressing is true only about MC68020 (1984) and later CPUs.
MC68000 and MC68010 had essentially the same addressing modes with 80286, i.e. indexed addressing with up to 3 components (base register + index register + displacement).
The difference is that the addressing modes of MC68000 could be used in a very regular way. All 8 address registers were equivalent, all 8 data registers were equivalent.
In order to reduce the opcode size, 80286 and 8086 permitted only certain combinations of registers in the addressing modes and they did not allow auto-increment and auto-decrement modes, except in special instructions with dedicated registers (PUSH, POP, MOVS, CMPS, STOS, LODS), resulting in an instruction set where no 2 registers are alike and increasing the cognitive burden of the programmer.
80386 not only added extra addressing modes taken from DEC VAX (i.e. scaled indexed addressing) but it made the addressing modes much more regular than those of 8086/80286, even if it has preserved the restriction of auto-incremented auto-decremented modes to a small set of special instructions.
There's a straightforward answer to the "why not" question: because it will result in codebases with the same kind of memory unsafety and vulnerability as existing C code.
If an LLM is in fact capable of generating code free of memory safety errors, then it's certainly also capable of writing the Rust types that guarantee this and are checkable. We could go even further and have automated generation of proofs, either in C using tools similar to CompCert, or perhaps something like ATS2. The reason we don't do these at scale is that they're tedious and verbose, and that's presumably something AI can solve.
Similar points were also made in Martin Kleppmann's recent blog post [1].
It is also equally odd to me that people want to cling so hard to C, when something like Rust (and other modern languages for that matter), have so much nicer eco systems, memory safety aside. I mean C doesn't even have a builtin hashtable or vector, let alone pattern matching, traits and sum types. I get this is about AI and vibe coding, but we aren't at a point yet where zero human interaction is reasonable, so every code base should assume some level of hybrid human/AI involvement. Why people want so badly to start a new code base in C is beyond me (and yes, I've written a lot of C in my time, and I don't hate it, but it didn't age well in expressiveness).
> It is also equally odd to me that people want to cling so hard to C, when something like Rust (and other modern languages for that matter), have so much nicer eco systems, memory safety aside.
Simplicity? I learned Rust years ago (when it was still pre release), and when i now look at a lot of codebases, i can barely get a sense what is going on, with all the new stuff that got introduced. Its like looking at something familiar and different at the same time.
I do not feel the same when i see Go code, as so little has changed / got added to it. The biggest thing is probably generics and that is so rarely used.
For me, this is, what i think, appeals for C programmers. The fact that the language does not evolve and has been static.
If we compare this to C++, that has become a mess over time, and i know i am getting downvoted for this, Rust feels like its going way too much in the Rust++ route.
Like everybody and their dog wants something added, to make Rust do more things, but at the same moment, it feels like its repeating the C++ history. I have seen the same issue with other languages that started simple, and then becomes monsters of feature sets. D comes to mind.
So when you see the codebase between developers, the different styles because of the use of different feature sets, creates this disconnect and makes it harder for people to read other code. While with C, because of the language limits, your more often down a rather easier way to read the same code. If that makes sense?
Proofs of what? "This new feature should make the 18 to 21 year old demographic happy by aligning with popular cultural norms". This would be difficult to formalize as a proof.
Memory safety in particular, actually UB in general (got to watch out for integer overflows, among other things). But one could prove arbitrary properties, including lack of panics (would have been helpful for a recent Cloudflare outage), etc.
In order to prove lack of UB, you have to be able to reason about other things. For example, to safely call qsort, you have to prove that the comparison is a total order. That's not easy, especially if comparing larger and more complicated structures with pointers.
And of course, proving the lack of pointer aliasing in C is extremely difficult, even more so if pointer arithmetic is employed.
In this context it's proofs of properties about the program you're writing. A classic one is that any lossless compression algorithm should satisfy decompress(compress(x)) == x for any x.
That's because the 1 instruction variant may read past the end of an array. Let's say s is a single null byte at 0x2000fff, for example (and that memory is only mapped through 0x2001000); the function as written is fine, but the optimized version may page fault.
A grim portent for their mental health, given the attempt to reframe a judgement that demolished them and called them "character assassins" as supportive.
Really, though, this is the first time I've ever looked at TechRights for real, and the whole place is very... Always Sunny meme.
Unfortunately graphics APIs suck pretty hard when it comes to actually sharing memory between CPU and GPU. A copy is definitely required when using WebGPU, and also on discrete cards (which is what these APIs were originally designed for). It's possible that using native APIs directly would let us avoid copies, but we haven't done that.
there were at least two renderers written for the CM2 that used strips. at least one of them used scans and general communication, most likely both.
1) for the given processor set, where each process holds an object 'spawn' a processor in a new set, one processor for each span.
(a) spawn operation consists of the source processor setting the number of nodes in the new domain, then performing an add-scan, then sending the total allocation back to the front end
the front end then allocates a new power-of-2 shape than can hold those
the object-set then uses general communication to send scan information to the first of these in the strip-set (the address is left over from the scan)
(b) in the strip-set, use a mask-copy-scan to get all the parameters to all the the elements of the scan set.
(c) each of these elements of the strip set determine the pixel location of the leftmost element
(d) use a general send to seed the strip with the parameters of the strip
(e) scan those using a mask-copy-scan in the pixel-set
(f) apply the shader or the interpolation in the pixel-set
note that steps (d) and (e) also depend on encoding the depth information in the high bits and using a max combiner to perform z-buffering.
Edit: there must have been an additional span/scan in a pixel space that is then sent to image space with z buffering, otherwise strip seeds could collide, and be sorted by z which may miss pixels from the losing strip
The output of this renderer is a bitmap, so you have to do an upload to GPU if that's what your environment is. As part of the larger work, we also have Vello Hybrid which does the geometry on CPU but the pixel painting on GPU.
We have definitely thought about having the CPU renderer while the shaders are being compiled (shader compilation is a problem) but haven't implemented it.
In any interactive environment you have to upload to the GPU on each frame to output to a display, right? Or maybe integrated SoCs can skip that? Of course you only need to upload the dirty rects, but in the worst case the full image.
>geometry on CPU but the pixel painting on GPU
Wow. Is this akin to running just the vertex shader on the CPU?
It just depends on what architecture your computer has.
On a PC, the CPU typically has exclusive access to system RAM, while the GPU has its own dedicated VRAM. The graphics driver runs code on both the CPU and the GPU since the GPU has its own embedded processor so data is constantly being copied back and forth between the two memory pools.
Mobile platforms like the iPhone or macOS laptops are different: they use unified memory, meaning the CPU and GPU share the same physical RAM. That makes it possible to allocate a Metal surface that both can access, so the CPU can modify it and the GPU can display it directly.
However, you won’t get good frame rates on a MacBook if you try to draw a full-screen, pixel-perfect surface entirely on the CPU it just can’t push pixels that fast. But you can write a software renderer where the CPU updates pixels and the GPU displays them, without copying the surface around.
Surely not if the CPU and video output device share common RAM?
Or with old VGA, the display RAM was mapped to known system RAM addresses and the CPU would write directly to it. (you could write to an off-screen buffer and flip for double/triple buffering)
I regularly do remote VNC and X11 access on stuff like raspberry pi zero and in these cases GPU does not work, you won't be able to open a GL context at all. Also whenever i upadte my kernel on archlinux i'm not able to open a gl context until i reboot, so I really need apps that don't need a gpu context just to show stuff
But I seem to recall there are dirt cheap hacks to do same. I may be conflating it with "resister jammed into DVI port" which worked back in the VGA and DVI days. Memory unlocked - did this to an old Mac Mini in a closet for some reason.
It's analogous, but vertex shaders are just triangles, and in 2D graphics you have a lot of other stuff going on.
The actual process of fine rasterization happens in quads, so there's a simple vertex shader that runs on GPU, sampling from the geometry buffers that are produced on CPU and uploaded.
I've got a mostly-written emulator (in Rust). It's very easy to emulate, possibly the best gameplay bang for the emulator coding effort buck aside from NES. My main intent in writing this emulator is getting it running on an RP2350 board, like Adafruit Fruit Jam or Olimex RP2350pc.
It should also be possible to get the next generation (SNES, Genesis) on such hardware, but it's a much tighter fit and more effort.
I almost mentioned it in the talk, as an example of a language that's deployed very successfully and expresses parallelism at scale. Ultimately I didn't, as the core of what I'm talking about is control over dynamic allocation and scheduling, and that's not the strength of VHDL.
Right. This is the binary tree version of the algorithm, and is nice and concise, very readable. What would take it to the next level for me is the version in the stack monoid paper, which chunks things up into workgroups. I haven't done benchmarks against the Pareas version (unfortunately it's not that easy), but I would expect the workgroup optimized version to be quite a bit faster.
To be clear, you can express workgroup parallelism in Futhark, or rather, if the compiler sees that you've programmed your problem in such a way that it can take advantage of workgroup parallelism, it will.
But you're right, it would be interesting to see how the different approaches stack up to each other. The Pareas project linked above also includes an implementation using radix sort.