Let’s Write a Game Boy Emulator in Python • Inspired Python

Let’s Write a Game Boy Emulator in Python • Inspired Python

Opcodes and Operands

An opcode is the action the CPU must carry out against the opcode’s operands, if any. So if 1 + 2 is an instruction to a human, the opcode would be + to a CPU, and 1 and 2 the opcode’s operands.

All the code you write will, one way or another, reduce itself to a set of instructions the CPU you are running the code on can understand. How many steps that takes depends on the language and tooling you use: write assembly language and the gap is small; write Python and it’s a yawning chasm. In many ways an interpreted language like Python is similar to a CPU in that the interpreter mimics much of the architecture that makes up a computer in order to provide a “unified” environment where you can write your code once, and reasonably assume it’ll run where the interpreter runs.

Thus the benefit of an interpreted language is that if you can write the interpreter and make it work on esoteric computer hardware then you can expect the much larger body of work written for the interpreter to run also. Python specifically is run on a virtual machine which I think is rather telling.

Consider this snippet of code:

>>> def add(a, b):
        return a + b
>>> import dis
>>> dis.dis(add)
  2           0 LOAD_FAST                0 (a)
              2 LOAD_FAST                1 (b)
              4 BINARY_ADD
              6 RETURN_VALUE
                ^--- Mnemonic            ^^^^^ Argument
              ^--- Offset

Using the dis module I can run any object through its dis.dis() function and disassemble the object into its constituent instructions. Though the terms Python uses is not a perfect one-to-one to how the Z80 CPU looks at things, I think it’s a reasonable facsimile. I recommend you disassemble a few things in Python to get a feel for how your code is understood by Python’s VM. Note that the dis.dis function does not show the opcode, but dis.get_instructions() does.

Likewise, you can ask Python to give you the byte-compiled code of our function add:

>>> add.__code__.co_code
By the way …

Sort of, I must point out that Python’s bytecode has all manner of allowances in its implementation to support the dynamic nature of Python. That means the reified bytecode is not a 100% mirror of the source code. That is not a problem with the Z80 though.

The bytecode is the condensed representation of our code and so it is with Game Boy Cartridge ROMs. The cartridge ROMs also store the data – graphics, music, and so on – and you wouldn’t necessarily be able to tell code from data in their raw byte form.

If you pick out a byte from a ROM that has the value 144 — how do you know it’s code or one tiny fragment of a piece of music?

Source link

Leave a reply

Please enter your comment!
Please enter your name here