@soegaard2 Thanks for the link! I’m doing a 6800 emulator in Racket right now but I’m planning to support more MPUs. I made a s-expr DSL for writing MPUs so I can theoretically support any of them, except surely slower than a real dedicated emulator (I didn’t really test performance yet).
As its my first time writing such software I was unaware of such tricks. I’ll see how they apply in Racket :slightly_smiling_face:
I guess it won’t be that difficult to optimize basic perfs as most of the DSL is macros, so stuff gets inlined as it compiles.
For example, right now operations are generated as class methods. I don’t know how it relates in performance to using a C switch, but classes in Racket are mostly compiled into closures I guess so the call overhead might not be that important. I’ll see when I get into optimization phase, right now I’m just focused on finishing the base implementation (even Decimal Adjust Accumulator, which is a pain, but I wanted to emulate everything :stuck_out_tongue: )
What is a macro?
@kennethdevel this might not be a useful description, but: y’know how you can write functions and classes to abstract over patterns in what your code does when run? macros let you abstract over patterns in what your code does when compiled. like how “for” loops are really just an abstraction over writing “while” loops in a certain way
or how pattern matching is an abstraction over lots of ifs, switches, and variable declarations
So we use macros to make coding easier?
Macros make many things easier, but more importantly they make many more things possible that previously were impossible. Racket for loops and pattern matching are both implemented as macros in regular racket libraries - they’re not “language features” like in other languages. In a language without macros, if the language didn’t offer pattern matching there’s no way you could build it yourself.
racket libraries can even create entire static type systems with compile-time checking and runtime type-directed optimizations
That’s pretty cool
I think so too :)
Is there any examples of small and useful macros so I can see how they look?
Here are some examples of syntax-parse in use: http://docs.racket-lang.org/syntax-parse-example/index.html
Reading it now, interesting text.
After checking racket/private/class-internal.rkt
, it looks like (send)
is doing a hash-ref
then a vector-ref
, so I guess it’s a bit slower than a C switch after all.
@jerome.martin.dev I was using a hash table for decoding, but wanted a jump table (something like a switch). I changed the decoder to use a vector, indexed by opcode, got a 30% speed up
In the racket/class
code it looks like method names are indexed in a hashtable that gives the method index, then the index is used to access a method vector.
profiling confirmed that decoding was one of the main bottlenecks
I guess I’ll have to do the same kind of optimization as you then, if it happens to be a bottleneck for me too
another point is memory, the colecovision for example uses mirrored RAM where the RAM block is mirrored 8 times, so a memory access can’t be a single vector-ref
I have some ideas to optimize this that I’ll try later
On my side, I’m letting the user provide a memory decoder specific to the machine he/she wants to emulate, so I don’t have to handle that, I just use a parameter like ((current-address-decoder) the-address)
to get the data from memory
the machine implementation can therefore use whatever data structure fits the best
sprites & graphics are working, but the emulation is still at ~8MHz
how do I add a few milliseconds of delay? (sleep 0.002)
does not seem to work here
the delay is to sync the emulation with the real clock speed at 3.58MHz