All right, so yeah, our last speaker for the day, for this year actually, is Tirmang
Gomez.
And this is his first time doing a talk in general, so he's very nervous.
Okay, so this is the title of my talk.
It's a bit long, but the short version is at the bottom, I'm just gonna talk to you.
I spent some time, a couple of years ago, making a Gimbo emulator, and I'm gonna talk
to you about it.
So wanting some introductions, that's my name, and if you want to reach out to me after
the conference, those are some of the ways.
I work as a software engineer.
I don't work on emulators, I use them sometimes, but it's not part of my work.
This is mostly just a hobby, so I've done all of this on my own time.
And I can emulate the Gimbo camera as well.
So this is what I'm gonna talk about today, points 1, 2, 3, I'm going to talk to you about
my particular emulator, how you can run it if you want to do so.
And afterwards, I'm gonna talk more generally about Gimbo emulation and how you can build
your own emulator.
I'll give some tips that I found that are useful for debugging.
And at the end, some lessons learned, and hopefully, if there is time, some demo.
So this is what my target audience here is mostly, for this talk, is mostly going to
be emulator beginners, emulator development beginners.
I find the Gimbo to be quite beginner-friendly.
One other reason is because it's very heavily documented, and there are other reasons as
well that I'll get to later.
If you're interested in Rust and WebAssembly, you're going to see a use case.
And if you're just generally a fan of this device, then you might enjoy that also.
So why make this in the first place?
The main reason I'm sure many people here will relate, or people making emulators is
the nostalgia.
I used to own one of these, so I want to know how it works.
Another reason, more generally speaking, this system is very attractive to emulate because
of the, there's a huge amount of software out there, so you can spend many hours just
trying games and seeing if they work.
And if they don't work, then you can spend many more hours trying to fix them.
And it's just something I do for fun.
I did it mostly, I don't work on it much these days, but every time I do, it's a lot of fun.
So it's made in Rust.
The selling points for Rust are performance and memory save.
My main selling point is that it has a very useful package manager and build tool.
It's very quick to prototype things, and I was able to put this together very quickly
actually.
And one of the other main reasons I want to use it is because of WebAssembly.
The support in Rust is great, so you almost get WebAssembly for free if you use Rust.
The tools are very nice.
And it runs on the website because it's WebAssembly can run on the browser, so it's very portable.
That's my phone, that's my PC.
It also runs natively.
It's not just WebAssembly.
So if you want to run it, these are the commands you need to run.
There's a native build, single command.
You give it the ROM and it will emulate it.
The web build, this is the few more commands because you have to deploy a web application.
So it's just, but it's very straightforward.
It just works.
And that's the link if you want to try it.
So I'm going to talk about the architecture and emulation.
So these are the two devices that I emulate.
The original Gameboy came out in 1989.
It was extremely popular.
It was designed to be as cheap as possible, so lots of games were made for it.
And it lasted close to 10 years.
There were a few revisions in between, but it was mostly the same system.
And then almost 10 years later, the Nintendo released the color version, which has still
a very similar shape.
And also internally, the system is also very similar.
So the Gameboy color is like a super set of the original Gameboy.
So these are the two devices that I target.
And I have to mention the Gameboy Advance.
It's a completely different system.
It's arm-based.
It was still backwards compatible, but it's very different under the hood.
So I don't support it for the time being.
So I'm going to talk about the architecture.
I'm going to, so if you open the original Gameboy, you'll see a bunch of stuff.
But for emulation purposes, we only care about those three chips.
One of them has the CPU and the pixel processing unit, which and the other chips are memory.
So I'm going to narrow, I'm going to limit this section to just talking about the CPU,
the pixel processing unit, which does graphics and at the end to wrap it all up, I'll talk
about the memory map that you, which is what allows the CPU and the pixel processing unit
to talk to each other basically.
So some basic stats about the CPU.
It has 8-bit registers and 16-bit registers.
It can do 500 things, has 500 instructions, a 16-bit address bus and an 8-bit data bus,
and it can run at two different speeds.
The original Gameboy could only run at four megahertz, but the Gameboy color could choose
between either of those two speeds.
So about the registers and some general information, it has general purpose registers.
These are here for intermediate calculations.
There's also a flag register, which has information about the last arithmetic instruction that
run.
So if you add two numbers together or subtract numbers together and the result is zero, this
register will tell you and other things.
The 16-bit registers are basically just the 8-bit ones, but used in combinations of two,
mostly just for 0.3.
The general purpose ones, it has the normal program counter with the address of in-memory
of the next instruction, a stack pointer for implemented subroutines, and there's a global
switch for interrupts, it's Boolean, so when you set it to zero, the CPU will stop listening
to interrupts, such as bottom presses, until you set it back to one.
So how can you model this in Rust?
It's very simple.
This is exactly what it looks like on mine.
The state is very simple, it's just a few fields for the registers.
So I'm going to talk about instructions.
This CPU has 500 instructions.
It has your typical instructions that you would expect, so memory reads and writes, arithmetic
and branch instructions, so jumps and calling to subroutines.
Some of the instructions can be conditional using the F register, and on this website
you can see them in color coded in a very nice table.
So this is at the core of the CPU, this is how you implement the instructions.
So you have to do the three things, first you have to fetch the instruction from memory
using the PC register, afterwards you have to decode the instruction, so that means figuring
out what instruction to run based on that byte that you just read, and you can do this
with a, in C++ you would use a switch statement, in Rust you can use a match statement.
And after you decode the instructions you have to run it, so those are the three things
you do, you fetch, you decode and you run, and you run it in a loop, in a loop and that's
what the CPU does.
So this is one example of an instruction, the code is very simple, this is a memory
instruction, I'm only going to comment on the return statement, this particular instruction
on the real CPU would take eight cycles of the clock, and we need to keep track of this
because afterwards we need to see this information to synchronize all of the emulator, otherwise
it would lead to bugs, so that's why I returned the number.
Another example of instruction, an arithmetic instruction and exit operation, this one takes
for cycles and it's arithmetic so it modifies the contents of the F register.
And you can look up how to implement every instruction on this PDF.
So you do this for 500 times, you might make mistakes but there are ways to fix those,
I'll get to those later.
So you do it 500 times and you will end up with a massive match statement or a switch
statement, but the code inside of each of the branches is very simple, but it's still
error prone.
This is an optional thing you can do, because this is going to run very frequently, it doesn't
hurt to turn that into a sort of binary search, so you can optimize the code a bit using,
in Rust this is very straightforward using the match statements.
So that's pretty much the CPU.
I'm going to switch to the pixel processing unit, this is the chip responsible for graphics.
So the Game Boy had an LCD panel, this size is 160 pixels by 144, total of 4 colors, more
on Game Boy color of course, and it runs at roughly 60 hertz.
And the way graphics works on this particular system is by a composition of three layers,
you have the window layer, the spread layer and the background layer, and then there are,
the CPU has registers, this device also has registers to program how you composite these
layers together.
So I'm going to go layer by layer.
So the first layer is the window layer.
This is usually reserved for things like game stats, it's fixed on the LCD, you can move
it around, but the graphics within the layer are not movable, they are constrained to a
grid.
Can anybody guess this game?
Yes, Link's Awakening, yeah.
So that's Link, Link is a sprite on the sprite layer.
So sprites are basically freely movable objects on the LCD, you can have 14 in total and they
come in two different sizes, programmable by registers again, along with other things
like color and position and orientation and things like that.
And finally the background layer, what I think is the most interesting one, it's basically
a grid of 32 by 32 tiles, each tile is 8 by 8, so the total size is 256 by 256, so it
doesn't fit on the LCD screen, but you can scroll it using registers.
So that's, and also furthermore, the scrolling wraps around so you can be clever and implement
infinite scrolling that way.
So it cannot, so there are more registers, I don't have time to talk about all of them,
but there's a link.
So by today's standards, this graphic-wise, this system cannot do much, but there are
games that are quite clever using these limitations.
So this is one example, it's not really a game, it's more of a technical demo, but still.
So this particular example is used in the background layer only, and it's modifying this scrolling
register, so it's actually moving it around the screen, however, it's changing the value
of the register on every single line, and what this accomplishes is like a vertical
stretching effect, and at the same time they are stretching the Nintendo logo horizontally
in memory, you can see right there, and in combination these two things looks like they
are zooming in the Nintendo logo, which is something that the gameboy cannot do in hardware,
but they work around this by combining hardware and software, so I think it's quite interesting.
And there are many more examples of games being clever, this is one.
So implementation-wise, this pixel processing unit is a bit more tricky to implement, like
on the CPU, and because of that it is a source of most of my bugs, and this game is easy
to recognize, it's Tony Hawk.
So the reason it's tricky to implement correctly is because we need to keep the CPU and the
pixel processing unit in constant sync, that's the reason I was returning the number of cycles
on each instruction before, and if you don't do it accurately enough it would lead to stuff
like this happening, however I found that most games don't really care, most games are
quite forgiving of inaccuracies, every now and then you will encounter a situation like
this, in this particular example the rest of the game looks fine, it's only the interesting
that is glitchy, and I think this is one of the reasons why the gameboy is a good emulation
emulator, beginning-friendly emulation project because you don't need to be super accurate
to emulate most games.
So yeah, this is how you would implement the synchronization, this is how I do it, so first
you on each iteration step you implement, you run the CPU for an instruction, it will
give you the number of cycles that it will take, and then you use that to synchronize
the rest of the components, so you feed it to the rest of the components so that they
catch up to the CPU, so you do this forever, basically this loop right here is the core
of this emulator, this is what the emulator looks like, there are a few things like getting
the image from the screen and so on, but conceptually this is an emulator, it's very simple.
So I've talked about the CPU and the pixel processing unit, both have registers, but
they are separate things on the circuit board, so the CPU needs to be able to modify the
registers of the pixel processing unit, and the way this is done is through memory, because
these registers, every register that is not a CPU register is exposed in memory, so by
reading and writing particular values to a particular address in memory, you can modify
the registers of these devices, and you can map the memory map a bit like this, you have
the characters right there, the video RAM and work RAM are the same size, because they
are those two chips on the circuit board, those two other chips, they are the exact
same chip, and there are other things, the buttons themselves are inside of these registers
I.O., so yeah, there are some regions that are a bit special, you are not allowed to write
to this region for some reason, and there are other details, this link has a technical
documentation of the rest of the map in detail.
So implementing the memory is quite easy, you just list every single component and every
single register, a bit like this, so you get the cartridge, the video RAM, pixel processing
unit registers, the buttons, sound registers, interrupt, controller, and then you need to
be able to read from them, so based on the address range you can you route it to the
appropriate device, and you need a similar method for writing values, some of the values
will be read only, so keep that in mind, so at this point maybe you will have a sort of
working emulator, but if it is your first emulator, as was my case, then you will run
into bugs, and there are a few things, and they can be a bit tricky compared to other
types of software I found, so there are a few strategies that I, sorry, so there are
a few strategies you can follow in order to track down bugs, the first one I could give
is just, because there is so much documentation about the Game Boy you can turn it into unit
tests, to unit test particular sections of the hardware, the other reason why the Game
Boy is so beginner friendly is you can actually run the diagnostics, there are available ROMs
you can run and it will tell you where you are, where you have issues, so if you make
a mistake on the CPU, which is likely, then this particular ROM will tell you what the
mistake was, and you can also integrate this into your testing framework to run in CIO
for extra credit, so the next one, the next tip is debugging, I am going to show debugging
using an example, so after you have an emulator, the logical step is to build a debugger for
it, because it will allow you to see how, it will teach you things about the games running,
but it will also teach you where you might be making mistakes, so in this particular
example, when I run this game, at the moment it doesn't work, so basically this is what
it looks like, it just gives you a black screen, so there is nothing going on, but if you spend
time making a debugger, then you can start finding clues, in this case, I spend sometimes
just getting the instructions, the registers, the disassembly, very useful, and in this
particular example, I know what the issue with this game is, so it is writing a value
from this address and expecting a value that is never there, so this address corresponds
to something called a DMA transfer, and what this tells me is that I have made a mistake
in this emulation, so I can go to that particular section of my project and fix it, but I haven't
fixed it yet, because I found it quite recently, and also I found that it is a lot more fun
to add debugging features than it is fixing the issues themselves, and I've been a bit
busy recently, so that's the end of my technical talk, and I'm going to finish with some conclusions,
this is my favorite glitch by the way, it only happens when you set the name to a particular
name, it is very weird, so writing an emulator, at least on a Gameboy emulator, is the easy
part of emulating a Gameboy, like I said, there's tons of documentation, and the hard
part of the work has been done by other people who have been kindly enough to write down
their findings, so I just have to read the information, interpret it, and turn it into
a program, so I keep that in mind when I move to the next system to emulate, because it might
not be as easy, so most games as I said are forgiving of inaccuracies, except this is more
of an issue with my emulator, but most games are forgiving of inaccuracies in the graphics,
so this is yet one other reason why it's friendly for beginners, and finally, WebAssembly and
Rust are great, if you just Rust, it's using WebAssembly, it's very natural, if the support
is great, and I have a small demo, it runs on the browser, so that's the LCD, I'm also
drawing the video memory and the color palettes, and one of the things you can emulate on the
Gameboy is, it came with a camera, so if you load the camera on this application, it will
request permission for the camera, but I've shown the picture at the beginning, so if
you cancel the permission, it will still boot, so it has a fallback, so it cannot get the
webcam, because I haven't given it permissions, but it can still put the file in there. I think
you can play games with it, but I don't know how it works, but that's the demo, so that's
it from me. Can I just break in? I'm leaving immediately, but if you go out, please continue
your questions and your discussion, please look around you and take any garbage that
you see from the room here and put it in the back, if a lot of people help, it's not much
work, otherwise we will be here forever. Thank you.
I have a question. Can I modify it in such a way that I can mess with the logic of the
game? The question was, can I identify particular things happening on the different games?
Do you know about these trainers? No, I don't.
Can I implement something like a game shark to cheat on games? Yes, I could. The emulator
is built as a library, so you can use it as a library and read and write arbitrary bytes
to arbitrary addresses, so you could potentially build something like that, yes.
Thank you. You also had a corporate check. You have a single loop, where every part we're
I'm like, see, what?
You still in your program?
Oh, she's like, okay.
Yeah, I know.
Well, my question was that
you have a single room
that has processes on the CPU.
Yes, it's, yeah.
What if you wanted,
what if you were emulating with Rust
a system where you want
to have different threads for different peripherals.
But they are all accessing the memory.
Wouldn't the Rust
have the same interview with that?
Um, so
can I use Rust to,
can I run things in different threads
with the first problems?
And probably yes,
but that was a kind of worms that I didn't want to open.
And also, if the system
was simple enough like this one, you don't really need to
optimize like that.
It can all run in a single thread.
But for a more complex device,
sure, I would have to
investigate more on that.
But I didn't have to do that on this one.
Yeah.
Why did you pick Rust?
Was there any reason that you did not
select C++?
Yeah.
Why did I pick Rust over
something like C++?
It's what I use Rust
for my personal projects.
It's what I like using it.
It's what I like using.
And you know Rust better than C++?
Yeah.
And the processor is a 6502 or is it?
So the processor,
the question was what the processor
is.
Yeah, it's not a 6502.
I think it's a mix of
a Psylog Z80
and an Intel 8080.
So it's like a combination of the two.
I think it is
I'm not really sure.
You split your match up into
the binary sets. Did you actually benchmark that?
Because I thought the compiler would have
just translated into a jump tape.
On mic, you know.
And we're going to get kicked out.
I'll be honest, I didn't benchmark that
everybody was in change.