Hacker News new | ask | show | jobs
by deaddodo 3087 days ago
They're using a relatively simple opcode map, but most instructions are logically redundant as can be seen here:

http://www.pastraiser.com/cpu/gameboy/gameboy_opcodes.html

The next step in emudev, with most compiled/systems languages at least, would be to create macros (such as OP_LD or OP_ADD) that generates static instructions at compilation. Another, cleanish method, in C is to generate a 256-length function pointer (void*) based static array and map those generated functions to that, to make the dispatch step simpler. Small trade off, performance-wise, but rarely matters for 8-bit CPU's (where you would usually use an opcode table over an instruction decoder).

2 comments

For Z80 emulation the most efficient method I found was indeed a big nested switch-case statement. Compilers turn this into a jump table (or several if there are gaps), but don't need the function entry/exit boilerplate if each instruction code block would be its own C function referenced through a function-pointer-table. The switch-case code is generated with a python script which uses the 'algorithmic decoding approach' described here: http://z80.info/decoding.htm

Here's what such a code-generated instruction decoder looks like: https://github.com/floooh/chips/blob/master/chips/_z80_decod... (this is for a "real" Z80 with all undocumented opcodes, so it has a lot more cases to handle than the simple Gameboy Z80 variant).

C macros would just take the place of your python script. It's literally the exact same idea.
The Gameboy CPU shares the same octal-structured opcode format as the Z80 from which it was derived, so it is possible to decode it even more concisely: http://www.z80.info/decoding.htm

You can see that in octal, 1xx are all moves while 2xx is all ALU ops.

Oh, it's definitely decodable. Most CPU's can be (though x86 post-386 wouldn't be fun), since that's what they're usually doing internally. But, with 8-bit CPU's, the opcode space is usually limited to a byte, which makes tables very doable and very efficient.

My rust-based GB emulator uses a decoder based on those very docs, in fact. Though, modified slightly since the GB's CPU only supports the CB-extended range operations.