Show HN: Z80 Compiler Visualization
8bitworkshop.com
8bitworkshop.com
Superoptimization: https://en.wikipedia.org/wiki/Superoptimization
I found this long abandoned project which is described in non-English, see the examples on the bottom of the page: http://www.ricbit.com/mundobizarro/superopt.php
For example Keil C51 compiler produces binaries about 2x too large and 2-4x too slow, even with maximum level optimizations.
Also, why is it so laggy? It feels like it's being sent to a server for compilation, but it looks like it's all in JavaScript. (And V8 is honking fast, should be nearly instant.)
After writing out all the opcodes I'd then have to work out the values to use for any relative jumps, once I knew how long each instruction had been.
Finally the whole thing would be POKEd into memory, and tested.
Writing code like that, without a compiler/assembler was simultaneously a lot of fun and utterly exhausting. I know I got into the habit of putting in random "nops" just in case I had to add new functionality in later revisions - that would allow some unconditional jumps to be patched into place to avoid having to change offsets, etc.
int func(int x) {
return x * 1;
}
...still produces unnecessary stack manipulation: 0000 C1 [10] 56 pop bc
0001 E1 [10] 57 pop hl
0002 E5 [11] 58 push hl
0003 C5 [11] 59 push bc
0004 C9 [10] 60 ret
Plain "ret" would have been enough.If the parameter is the second item down on the stack and the compiler wants the result in hl with the stack unchanged.
pop bc
pop hl
push hl
push bc
isn't redundant stack manipulation. it's moving stack values to bc and hl while keeping the stack unchanged.Of course at another level a compiler should be able to inline x=>x*1 but that depends on the build architecture. If it's a linkable function then the contents may be opaque to the code that calls it.
In SDCC function params are always passed on stack, but return value in HL. So pop hl/push hl moves function parameter to return value register HL. (pop/push bc is of course just used to get past return address value in stack.)
Seems odd compiler doesn't pass parameters in registers in a relatively register rich z80 arch.
It's particularly neat to watch how it handles things like "x * 16 + 500000" by splitting integers and using bit rotation.
when I found out people were moving the stack pointer register to the screen memory base address and using stack pushes (instead of mov) to move data - to save a clock cycle or two each byte move - it blew my mind - hacks like that would be common knowledge in minutes now
So the user might occasionally see interrupt stack frame on screen?
also, on that Uridium vid - notice how they are using the standard font but spacing it out vertically (so they could save memory)
Leaving those out must have saved some memory too.
----
Shameless plug for an abandoned project of mine: (note: these demos appear to work in the latest Chrome on a wide monitor - YMMV; I didn't know what I was doing.)
Back in 2012 I wanted to learn JS and had some ideas about a UI for teaching C programming with good visualizations. I was interested in building tools that would allow you to visualize in-memory data structures and step through the code (so you could watch a sorting algorithm, or tree manipulation, that kinda stuff.)
Anyway the full thing never panned out but I've got some lower-level demos:
Here's a UI for running MIPS-like assembly: http://csclub.uwaterloo.ca/~j3parker/things/evalc/evalc3/mip...
(Hit "run" and click "faster" a few times) It started off as straight-MIPS but I realized emulating a real machine wasn't necessary for the overall goal (teaching C and the C abstract machine) so I started adding convenient op-codes. All words (I think) of memory had tags to specify if the memory was initialized (maintained by the VM) stack/heap (maintained theoretically by the C runtime) or code/data. The goal was to make a C machine that was maximally friendly for education.
Here's a C REPL that compiles to that assembly and runs it on the VM: http://csclub.uwaterloo.ca/~j3parker/things/evalc/evalc/ it compiles on keyup (I had vague goals of very quick feedback) and there is a line-edit below the source code that you can put C expressions into (e.g. type "fib(12)" without the quotes and hit enter - it should print 233 if you haven't modified the code.)
The parser is mostly-complete C99 (typedefs were an issue - the first parser used a YACC-like generator which is known to have troubles here - the next version used a hand-rolled recursive descent parser. That one didn't get finished but also had way better error messages, as expected.) The compiler (semantic analysis + codegen) didn't support much of the language though, e.g. it doesn't know how to code-gen for > but it knows <. So, the compiler is not at a very usable state.
I also had a V2 of the VM with an insane fixed 8-bit instruction set (with 200+ instructions) for a stack-based (not register) machine with a 32 bit address space. It would generate (with a poorly written bash script) the VM from the LaTeX documentation for the CPU which was weird. Here's the PDF: http://csclub.uwaterloo.ca/~j3parker/things/evalc/evalc2/vm/... the .tex file (in the same directory) has the impl of the instructions (not rendered to the PDF - that was the intent though) and it generates this: http://csclub.uwaterloo.ca/~j3parker/things/evalc/evalc2/vm/... It never did get finished but it had some promise.
---
It was a fun short project that got abandoned. I still think there would be value in an educational implementation of C, something that conforms to the spec but is in no way performant--instead, UB sanitizers everywhere and special hooks inside the runtime to aid in visualization and debugging (e.g. I want to look at memory and see all the allocations with links back to what line of code allocated this word of memory, etc.)
Source code (it's all really bad):
- VM: https://github.com/j3parker/mipsjs/blob/master/mips.js
- Jison grammar/parser: https://github.com/j3parker/jcc/blob/master/c.jison
- Better, but incomplete recursive-descent parser: http://csclub.uwaterloo.ca/~j3parker/things/evalc/evalc2/par...
- Compiler: https://github.com/j3parker/jcc/blob/master/jcc.js
I especially like that you can pass compiler options, like -O3 and see the magic :)
I wish I had something like that back in the days when I was optimizing crap out of my code
P.S. SDCC mirror in GitHub: https://github.com/svn2github/sdcc
It is a companion to the book, "Making Games for the Atari 2600". A wonderfully clear and concise primer that I thoroughly enjoyed, even though I had no interest in the Atari 2600 per se. The NES was my first console -- and naturally the one I'd always wanted to program. Since it shares the same 6502 processor, I used this book as an introductory text before diving into the murkier waters of NES development wikis and forums.
Usually it's just as fast. But on some platforms, you need to first clear carry bit, because they only have rotate and rotate through carry instructions available.