
A MOS6502 assembler impleme​nted as a Rust macro - mmastrac
https://play.rust-lang.org/?gist=a18d697454f9261b28ff&amp;version=stable
======
monocasa
Reminds me of this sweet paper

[http://research.microsoft.com/en-
us/um/people/nick/coqasm.pd...](http://research.microsoft.com/en-
us/um/people/nick/coqasm.pdf)

~~~
nickpsecurity
That's the exact one I thought of. On top of it, there's typed assembler:

[https://www.cs.cornell.edu/talc/](https://www.cs.cornell.edu/talc/)

And Vx86 used in Microsoft hypervisor:

[http://swt.informatik.uni-
freiburg.de/staff/maus/dissemain.p...](http://swt.informatik.uni-
freiburg.de/staff/maus/dissemain.pdf)

Note: Microsoft also used a TAL, though can't recall if same as Cornell, in
their VerveOS that was verified down to assembler. Combined with SLAM, it's
safe to say their researchers are kicking serious behind on practical,
cutting-edge verification. I wouldn't have believed it 10+ years ago if
someone told me haha.

------
hathawsh
Very cool. As a Rust newbie I'd like to understand what's going on here. Is
this a "procedural" macro? Does Rust specifically target writing DSLs using
its macro system? Can we expect macros like this to continue working for a
long time? There's a blog post that suggests possible changes:
[https://internals.rust-lang.org/t/the-future-of-syntax-
exten...](https://internals.rust-lang.org/t/the-future-of-syntax-extensions-
and-macros/2889)

~~~
mcpherrinm
This isn't a procedural macro: Procedural macros run Rust code to transform
your program, and interact with the guts of the compiler (AST, etc). That's
hopefully going to change to not be compiler-version specific, etc.

This is using a system known as "macro_rules" which are a system of
"expanding" \-- in some ways it's a fancier version of the C preprocessor, but
also resembling the Lisp/Racket macro system to some degree too.

Let's take a random little piece of that code from the post and talk about it:

    
    
        macro_rules! codelen {
            () => { 0 };
            ( $($c:expr),+ ) => { [$($c),+].len() };
        }
    

This declares a new macro substitution rule called `codelen`. Without going
into too much depth, that's defining rules (on the left hand of the `=>`) and
saying what code to expand them into, on the other side. The first replaces
`()` with `{0};`, just a simple substitution. In the second rule, the
`$($variable),+ is the essential construct here, letting you operate on
sequences of input stuff.

It's a little hard to read -- writing your own macros in Rust is a little bit
complicated and I don't think it's actually something that should be done
often. You can write little DSLs with it for sure. This variety is going to
stick around and will keep working. It is called macro_rules! and not just
macro! to leave that name free for a future, enhanced way to _write_ macros,
though.

------
GlitchMr
It doesn't support explicit accumulator addressing mode (ASL A, LSR A, ROL A,
ROR A), but otherwise quite neat. (from a quick look at it)

(also, 0x syntax is quite unusual, but I assume $ couldn't have been
implemented for some reason)

~~~
ddingus
The accumulator is the only register those work with. It was very common to
not specify it.at all. I actually never have in all the 6502 assembly code
I've ever written. Wasn't required.

~~~
pvg
I don't think that's true.

[http://www.downloads.reactivemicro.com/Public/Users/David_Cr...](http://www.downloads.reactivemicro.com/Public/Users/David_Craig/Apple2OriginalROMInfo.TXT)

Code is straight from the Red Book listings. Search for ROR - accumulator
(shown explicitly), indexed, you name it. 4 modes each.

~~~
ddingus
Well, I have written a lot of code on 6502.

What you name is the value to be operated on, and that is indexed, zpage,
immediate, etc... the accumulator is not named. There are two or three bytes,
one instruction, one or two for value or address.

Same for things like ADC, the accumulator is where it will happen, the things
you name are values and addresses.

LSL, LSR can just be stated, one byte instructions that operate on the
accumulator, which does not need to be specified.

6502 is really basic. The accumulator is the working register for many things.
The index registers are for indexing. One does not do LDX $c000, A for
example.

~~~
pvg
I believe you. But it was, quite typically, like you can see in the Woz code
above. Or from '6502 Assembly Language Subroutines', 1982 vintage.

[http://i.imgur.com/jreIWA5.png](http://i.imgur.com/jreIWA5.png)

~~~
ddingus
Yeah, it can be. No worries. Guess I never assembled on something that
required it.

Fun old times in any case. That chip provided just enough, and no more.

------
gamache
No ORG, no deal. I can't use this in production unless it supports ORG.

------
CodeWriter23
That's a new twist on the Macro Assembler.

------
ilurkedhere
Lisp user here.

This reminded me of
[https://github.com/kingcons/cl-6502](https://github.com/kingcons/cl-6502)
where the opcodes are defined using a defasm macro
[https://github.com/kingcons/cl-6502/blob/master/src/cpu.lisp...](https://github.com/kingcons/cl-6502/blob/master/src/cpu.lisp#L183)
and implemented here
[https://github.com/kingcons/cl-6502/blob/master/src/opcodes....](https://github.com/kingcons/cl-6502/blob/master/src/opcodes.lisp)

Are Lisp macros pretty much on par with Rust macros?

~~~
FreeFull
I was under the impression Lisp macros are more powerful/flexible than Rust's
macro_rules! macros, although I don't know enough about Lisp macros to be
sure.

------
JustSomeNobody
That ... is ... f __*ing ... ugly to look at.

~~~
vardump
Well, a creative abuse of the language. Programmer humor.

~~~
JustSomeNobody
Being a developer myself, I'm well aware of this. Still, it's ugly.

~~~
Animats
When I see this, I understand why the Go crowd didn't put generics or macros
in Go. It's so easy to write useful but awful things with them, and they're
really hard to fix and debug.

This is getting to be a big problem with Rust. The language, or maybe its
community, encourages cutesy stuff like this. Remember "Diesel", the Rust
compiler for SQL, from a few days ago? Just because you can doesn't mean you
should. Too much fancy template stuff is being baked into the low level
libraries. This may not end well.

C++ went down this road. The template system turned out to be more powerful
than intended. Then people started writing cool stuff as fancy templates. Then
the C++ committee added features to support the fancy templates better. Overly
elaborate templates damaged C++; nobody could figure out what was going on.
Use of C++ for new work declined.

All of this comes from smart people with good intentions. But when there's too
much of this stuff, the mental load required to keep up with all of it becomes
excessive. Especially if it changes a lot.

~~~
pcwalton
This is silly. Rust could not achieve its performance and safety goals without
macros and generics. Generics may have been optional for Go (I don't agree,
but let's go with it), but _there is no way to achieve zero-cost memory safety
without generics_ (while supporting first-class references and dynamic
allocation).

Rust generics are deliberately less expressive than C++ templates, in that
they're strongly typed. That's why we get so many complaints from C++ and D
developers that you can't do certain things. But Rust sticks with its
strongly-typed generics, because Rust has always tried hard to strike a
balance between code expressivity and maintainability.

This is a terrible example if you want to prove that point, anyway. This 6502
assembler is obviously a total abuse of the system, and nobody will deploy it
to production. It's a neat hack, and that's all. Nobody would use an
obfuscated quine written in Go to argue that Go is unreadable. This is exactly
analogous.

Finally, Diesel is just an ORM. It uses exactly enough features to be an ORM;
the only "problem" with it is that it's an ORM.

~~~
vardump
Animats had a very important point. While magic is so attractive and perhaps
even useful in moderation, it causes so much damage in a large code base.

In C++, you get this damage from for example operator overload abuse (ok for
math, not many other defensible uses), template abuse (ok in moderation),
ninja exceptions and inheritance (especially multiple inheritance, ugh).

Anyways, if Rust proves to be a feasible workhorse building medium/large
systems with bigger teams, it does sound interesting to me. Especially
catching concurrency issues at compile time.

I just hope it won't ever become another C++.

~~~
pcwalton
And my point is that nobody would ever deploy this to production. Would you
say you're worried about Go becoming C++ if someone posted a quine written in
Go to HN?

By the way, Rust has no "template abuse", "ninja exceptions", or
"inheritance".

~~~
Animats
You end up with it in production because some lower level library uses it.

~~~
pcwalton
No Rust production program will ever transitively depend on a library with a
6502 assembler written in the macro language.

~~~
pjmlp
You are missing the point.

It is not the ORM library or the 6502 Assembler.

It is the library written by the clever guy on the third floor from blue team,
for the new product being developed at Corpo X.

However I also have seen lots of convoluted code, exactly because such
expressiveness is missing from the language.

For example, JVM bytecode manipulation or use of external code generators.

~~~
Manishearth
> However I also have seen lots of convoluted code, exactly because such
> expressiveness is missing from the language.

That's the operating point here. Leave out expressivity, and tons of
programmers write convoluted code. Leave it in, and a couple of clever people
use it in an over-clever way.

------
mmastrac
The author appears to have a crate for it as well: [https://github.com/jonas-
schievink/rustasm6502](https://github.com/jonas-schievink/rustasm6502)

------
vvanders
Link appears to be broken, working one is:

[https://play.rust-lang.org/?gist=a18d697454f9261b28ff](https://play.rust-
lang.org/?gist=a18d697454f9261b28ff)

