
Show HN: Z80 Compiler Visualization - sehugg
http://8bitworkshop.com/viz.html
======
richard_shelton
It would be nice to have a superoptimizer for Z80 in a form of web tool. You
provide a C expression to it and the tool will return in turn the shortest
code sequence. Demosceners would love it.

Superoptimization:
[https://en.wikipedia.org/wiki/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](http://www.ricbit.com/mundobizarro/superopt.php)

~~~
vardump
Us firmware developers would also love it! Something like that could easily
save real money. 8-bit C-compilers are pretty bad.

For example Keil C51 compiler produces binaries about 2x too large and 2-4x
too slow, even with maximum level optimizations.

------
trevyn
Nice, but I wouldn't call it a visualization. More like a "live preview" or
"instant compiler" or something.

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.)

~~~
sehugg
It's all done in a worker running asm.js modules. I've found it to be fastest
on Firefox. Would love to speed it up; haven't found the magic emscripten
flags yet.

~~~
sehugg
Actually, I'd like to try WebAssembly, but I keep getting "TypeError:
Module.asm.__GLOBAL__I_000101 is undefined" errors when trying to run it.

------
Xcelerate
This brings back a lot of memories. I remember in high school printing out a
sheet of Z80 opcodes (at size 6 font), writing a program on a sheet of paper,
and then manually compiling that program and typing in the hex codes (using
the AsmPrgm instruction to prefix the program). It was great for boring
classes as long as you sat far enough in the back. As a prank, we started
grabbing people's calculators and turning off the LCD display using some
sequence of assembly. Restarting the calculator wouldn't fix it, so you either
had to memorize the button sequences to re-enable the display or remove the
batteries and let it sit for a while.

~~~
chairmanwow
How on earth did you work out how to program assembly through button presses
on a calculator? Sounds like a stunning prank though.

~~~
Xcelerate
The TI graphing calculators have a mode for writing your own programs.
Typically this is done using TI-BASIC, but if you prefix the program with the
AsmPrgm symbol, it will allow you to directly write Z80 opcodes as hex values.
Combined with a TI-provided guide for making bcalls (calls into common
routines that have already been written, like changing LCD settings or
multiplying floats), it's not too difficult to write a short ASM program by
hand.

~~~
mikeknoop
Fun times. I did the same hand compiling back in middle school on a couple
long plane flights where I didn't have access to a full compiler. Shame this
never shipped:
[http://www.detachedsolutions.com/main/oldnews.php?item=58](http://www.detachedsolutions.com/main/oldnews.php?item=58)

------
vardump
Code generation is probably missing somewhat trivial optimizations. Here's one
example:

    
    
      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.

~~~
Lerc
Depends on how it expects the parameter and the result.

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.

~~~
vardump
Ok, seems like you're right.

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.

------
rcarmo
This is amazing.

It's particularly neat to watch how it handles things like "x * 16 + 500000"
by splitting integers and using bit rotation.

~~~
mtkd
at the time Z80 was popular ('87 ish) there were few/no tools outside of
Hisoft Devpac
([http://www.secarica.ro/images/zx/genp351.gif](http://www.secarica.ro/images/zx/genp351.gif))
neither was there any internet / forums etc. to learn how others were solving
things - like that bit rotation example

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

~~~
vardump
> 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 ...

So the user might occasionally see interrupt stack frame on screen?

~~~
mtkd
memory is hazy, but using SP to move the memory was fast enough you could blit
most (not all) of a screen in one refresh cycle - which is, I think, why games
like Uridium
([https://www.youtube.com/watch?v=TvULd4zHz8Y](https://www.youtube.com/watch?v=TvULd4zHz8Y))
were running the main gameplay in a smaller area

also, on that Uridium vid - notice how they are using the standard font but
spacing it out vertically (so they could save memory)

~~~
vardump
Also no level destruction scene. Intra-level minigame was also completely
missing.

Leaving those out must have saved some memory too.

------
geori
Ahh. I remember doing lots of TI82/83 calculator programming in Z80 and never
considering using a C to Z80 compiler since it was so suboptimal. C was
considered slow and a memory hog. Most of these (other than the sprite
routine) aren't that bad. Every byte counted when you only had 26KB free.

------
jacobparker
I really like stuff like this. This tool is amazing for C++:
[https://godbolt.org/](https://godbolt.org/) It's really handy for popular
architectures and it's nice that it has "real" compilers (GCC vs. clang vs.
ICC.) I find it fun to compare small pieces of code between the different
compilers.

\----

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...](http://csclub.uwaterloo.ca/~j3parker/things/evalc/evalc3/mips/)

(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/](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/...](http://csclub.uwaterloo.ca/~j3parker/things/evalc/evalc2/vm/docs/cpu.pdf)
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/...](http://csclub.uwaterloo.ca/~j3parker/things/evalc/evalc2/vm/vm.js)
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](https://github.com/j3parker/mipsjs/blob/master/mips.js)

\- Jison grammar/parser:
[https://github.com/j3parker/jcc/blob/master/c.jison](https://github.com/j3parker/jcc/blob/master/c.jison)

\- Better, but incomplete recursive-descent parser:
[http://csclub.uwaterloo.ca/~j3parker/things/evalc/evalc2/par...](http://csclub.uwaterloo.ca/~j3parker/things/evalc/evalc2/parser.js)

\- Compiler:
[https://github.com/j3parker/jcc/blob/master/jcc.js](https://github.com/j3parker/jcc/blob/master/jcc.js)

~~~
faragon
Thank you very much, it is very interesting! I started with a weird VM with
16-bit opcodes for variable-length instructions (VLIW) and now I'm trying to
write a compiler for it. At the beginning I considered a stack-based CPU so I
could use less bits per opcode, and implement Forth-like VLIW stuff, but I
discarded it for a register-based VLIW VM, so I could write a C compiler
easier.

------
ravishah
I used to love the zilog z80, so super happy to see this Used to use it for
automation and running FORTH...

------
faragon
Still not being very efficient generating Z80 code, the SDCC is amazing! :-)

P.S. SDCC mirror in GitHub:
[https://github.com/svn2github/sdcc](https://github.com/svn2github/sdcc)

------
sytelus
Given that Z80 is super easy to learn assembly language, this is a great
introduction on how C code would internally. I would suggest give the option
for 8085 too as the its so much similar.

~~~
andrepd
Z80 isn't actually that easy. It's a quirky little chip, I'd much sooner
recommend the 6502 for an assembly beginner.

~~~
ucs
As luck would have it, the very same site under discussion also happens to
host a neat online "IDE" for learning all about the 6502-based Atari 2600¹.

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.

¹ [http://8bitworkshop.com](http://8bitworkshop.com)

------
dorfsmay
uh! Adding a number to itself for multiplication by 2 instead of one quick
shift to the left?

~~~
sehugg
The Z80 has no 16-bit shift instruction, but has 16-bit add/subtract :)

------
Digit-Al
Ah. Good old Z80. For some reason, C9 has become indelibly burned into my
brain.

