Heads up! To view this whole video, sign in with your Courses account or enroll in your free 7-day trial. Sign In Enroll
Preview
Start a free Courses trial
to watch this video
Cross-Platform: Computer Science, Emulation, and the NES with Kevin Zurawel
20:23 with TreehouseKevin discusses at a high level about how emulators are developed, the hardware & software of the NES & about how computers as a whole function.
This video doesn't have any notes.
Related Discussions
Have questions about this video? Start a discussion with the community and Treehouse staff.
Sign upRelated Discussions
Have questions about this video? Start a discussion with the community and Treehouse staff.
Sign up
[MUSIC]
0:00
Good morning, everyone.
0:04
Hope you're having a lovely
Treehouse Festival day.
0:06
It's so much fun having you all here.
0:09
I'm Ryan Carson, the co-founder and
CEO of Treehouse, and
0:11
I'd like to welcome up our next speaker,
Kevin Zurawel.
0:14
Kevin has been an NES fan
since he first first played
0:18
Super Mario Brothers back in 1989.
0:22
He learned to program out of
a love of video games and
0:25
ended up with a career in web development.
0:27
Kevin is currently an engineering
manager at Braintree in Chicago.
0:30
He has previously spoken on computing
history and game development topics at
0:34
conferences like Abstractions, Node.js
Interactive, ForwardJS, and Strange Loop.
0:39
It's fun to have a fellow
NES fan on stage.
0:45
So please give a warm welcome to Kevin.
0:48
>> Hey, everybody, my name is Kevin.
0:51
I have been writing some code for
the NES for about four years now and
0:53
I would love to be able to tell you about
how you can go about kind of making an NES
0:57
emulator yourself.
1:01
And along the way,
we'll learn how computers work in general.
1:03
Let me just share my screen here.
1:07
All right, so the original version
of this talk was called So
1:12
You Want to Write an NES Emulator.
1:17
That's what we'll be talking about.
1:20
First off,
NES you heard this a bunch of times.
1:22
What are we talking about?
1:25
This is the Nintendo Entertainment System,
1:26
released by Nintendo in the US 1985,
was available for about 10 years.
1:29
And it really kind of defined what video
games were for an entire generation.
1:34
We also need to talk about emulators.
1:40
Emulators are basically just software
in our case that enables one computer
1:42
system to pretend that it
is another computer system.
1:47
So in our case, we're gonna be talking
about how you can use your computer
1:50
to pretend that it is an NES emulator or
an NES.
1:54
NES emulators are very popular.
2:00
If you look on GitHub, there are over
1,700 repositories people have made for
2:02
NES emulators.
2:07
Here is kind of a variety of some
of the most common ones you'll see.
2:09
And I just wanna point out, these NES
emulators have been written in practically
2:11
any programming language
you could imagine.
2:15
So regardless of what language
you are interested in using,
2:18
someone probably has a project you can
look at or you could start your own.
2:21
So how does the NES work
at a very high level?
2:29
When you want to play your NES game,
you need two things.
2:34
You need a hardware and yes, console.
2:37
And you need a cartridge
that has your game on it.
2:40
You put one inside of the other.
2:44
If we look inside of that
console on the motherboard,
2:46
you basically have these
two giant chips here.
2:49
The one on the left is called the 2AO3.
2:52
And the one on the right
is called the 2CO2.
2:54
The one on the left is the central
processing unit for the NES, the CPU.
2:57
And the one on the right is
usually referred to as the PPU or
3:02
the picture processing unit.
3:06
It's the chip that creates all of
the graphics that show up on the screen.
3:08
If we look inside of the cartridge, you
have a much smaller little board here and
3:13
it also has two big chips, one called PRG.
3:17
That's the program ROM that has
all of your games code on it and
3:20
the other one is called CHR.
3:24
That's the character ROM and that has all
of the graphics data for your game, so
3:26
two big chips on the motherboard,
two big chips on the cartridge.
3:31
When you put the one inside the other,
3:35
what you're really doing is you are wiring
that PRG-ROM directly into the CPU,
3:38
you're wiring that CHR-ROM directly
into the graphics processor.
3:42
Now, you may be wondering how do you put
a cartridge inside of your computer?
3:49
Not a thing you can really do, instead we
end up using what people call ROM files.
3:54
These usually end in .nes and
they're a pretty standardized format.
3:59
There's a 16 byte header at the beginning,
4:03
which tells the emulator about what
kinds of things are on the cartridge.
4:06
This was a standard that was actually
created by a program called iNES,
4:11
which is one of the first NES emulators.
4:15
And every other author of an emulator
thought this was a great idea,
4:16
just copied it.
4:20
After the header, you get all of the
contents of that PRG-ROM, the game data.
4:21
And after that, you get all the contents
of the CHR-ROM, the graphics data.
4:26
So we've got our cartridge in a file.
4:31
How do you actually turn that cartridge
data into a game on your screen?
4:33
At least, how does the hardware NES do it?
4:38
Well, the NES is using what
is called machine code.
4:41
Basically, any processor runs
its own version of machine code,
4:44
which is just a stream
of bytes in this case.
4:49
The processor is gonna
look at these bytes.
4:53
Each byte is gonna tell it to
do a different kind of thing.
4:54
And it's just going to follow
the instructions as it's told.
4:58
In our case, the processor within the NES
is a modified version of this chip
5:03
called the MOS Technologies 6502 and
it came out in 1975.
5:08
It was already ten years old
when the NES started using it.
5:12
It was a very popular chip.
5:17
Apple II used it.
5:19
Commodore 64 used it.
5:20
Lots of computers of
this era used this chip.
5:21
It has 8-bit registers and instructions,
which means each of those bytes
5:24
that we saw in the machine code is
one instruction for the processor.
5:29
And it has a 16-bit address bus,
5:34
which means it can talk to 64
kilobytes worth of memory.
5:37
Those instructions,
the 6502 knows 56 of them.
5:44
So this is the entirety of
what this processor can do.
5:48
Anything that your game is going to do or
that code for this chip in general is
5:51
going to do is gonna be some
combination of these 56 instructions.
5:55
What I'm showing you here
are these three letter codes.
6:00
This is how these instructions would show
up in assembly code which you would write
6:02
for this processor, which would
then get turned into machine code.
6:07
One instruction I'm gonna be
talking about a lot here is LDA,
6:10
kind of in the middle of your screen.
6:13
It's Load Accumulator with value.
6:14
We'll talk about what that means.
6:16
So one thing that kind of set the 6502
apart from other processors of its time
6:20
was that it had this idea of
multiple addressing modes.
6:25
So you could use one instruction like LDA.
6:28
And you could use it in different ways by
using these different addressing modes.
6:31
So an example would be
this immediate mode.
6:35
You could say I want you to load a
specific value that I'm just giving to you
6:38
directly versus absolute mode, which is
I want you to go look up a value from
6:43
some memory address and use that to load.
6:48
Or the index mode, which is I want you to
look up something at a memory address and
6:51
then add something to it and
then use that to return back to me.
6:55
What this means essentially is for
every combination of an instruction and
7:01
an addressing mode,
you get what we call an opcode.
7:04
This is the actual byte that
shows up in the machine code.
7:07
So when the processor
sees a byte that is A9,
7:11
it knows that's LDA with
immediate mode addressing.
7:13
Or when it sees AD, it knows that's
LDA with absolute mode addressing.
7:17
6502, like basically all processors,
also has these things called registers.
7:25
They are places within the processor that
are temporary storage that can also do
7:30
some other things.
7:35
You can think of them kind of like
variables in a programming language.
7:36
There are things that just
temporarily hold a piece of
7:40
data while you work with it.
7:43
These are all the registers
the 6502 has which is pretty small.
7:45
A modern like Intel or
AMD processor is going to have dozens of
7:49
registers available for
you to use, the 6502 [INAUDIBLE].
7:53
One in particular I'm gonna point out
here is called the program counter.
7:58
It usually gets abbreviated to PC.
8:02
The program counter just holds the memory
address of the next instruction that's
8:04
going to be executed.
8:09
So the 6502 kind of reads in a byte,
figures out what it needs to do,
8:11
and then it increments up
the program counter to figure
8:16
out the next thing it needs to read.
8:19
This is also how the processor can
do things based on what happens,
8:22
go to a different part of your code,
instead of just continuing linearly.
8:26
It, as I mentioned, knows how to talk
to 64 kilobytes worth of memory.
8:32
These 64 kilobytes are the entire universe
as far as the processor is concerned.
8:37
It has no concept of dealing with files or
8:42
anything kind of outside of these
64 kilobytes worth of memory.
8:45
The way those get broken out inside
the NES is that half of those 64
8:51
kilobytes are the PRG-ROM
file from your cartridge.
8:56
So your game code just kinda gets
slotted directly into the processor.
9:00
And then it uses what's left to
do things like RAM or input and
9:04
output, which we're gonna
look at in a minute.
9:08
The NES uses what it calls memory-mapped
I/O or actually, the 6502 does.
9:13
This is how the processor can communicate
with other parts of the system if it wants
9:19
to talk to the graphics processor
to send it some graphics data.
9:23
Instead of reaching out in a special way,
there are specific memory addresses
9:28
that are actually communication
pathways to these other chips.
9:33
So for example here,
if we read memory address 2002,
9:37
we get the status of what the graphics
processor is currently doing.
9:40
Or we can send data to
the graphics processor by
9:44
writing to memory address 2007.
9:47
That's kind of like a very high level
overview of how this all works.
9:53
But let's look at some actual code
like how an emulator would work and
9:56
how you would build this.
9:59
So the first step is we need to
track the state of the system.
10:02
We basically need to set up variables
in our code that are going to be,
10:06
here's all the memory for the CPU.
10:10
Here's all the registers.
10:12
We need to set up that program counter,
PC.
10:14
Here we're setting it to what's
called the reset vector address.
10:18
This is actually a hard coded memory
address inside the processor,
10:21
where when you first turn it on, it looks
for whatever is in that address, and
10:24
that tells it where to
start running its code.
10:28
And then we basically just start a loop.
10:34
So as long as the system is running,
10:36
we're just gonna have basically
like a big switch statement.
10:37
Which is figure out whatever is in
the memory address of where the program
10:41
counter is, and then call some
function depending on what you get.
10:46
So to call those functions, we need to
implement the instruction set of the 6502.
10:51
We're basically gonna
write one function for
10:59
every opcode that the processor
knows how to handle.
11:01
So in this case, if we see an 89,
that's the opcode.
11:04
That means LDA immediate mode.
11:08
What we do is we get
the next thing in memory.
11:11
We put it into the accumulator, and
we increment our program counters, so
11:14
we're ready to start
that loop all over again.
11:17
So a lot of these functions are very,
very small.
11:21
Nothing in the instruction set
is particularly complicated.
11:24
It's just that you need to write a lot
of these because there are about 160 or
11:28
so opcodes when you combine all
those instructions with different
11:33
addressing modes.
11:37
So you're gonna write one function for
every single thing in this table.
11:38
Once you got that, we can start
looking at some graphics data.
11:44
The PPU has its own internal memory.
11:49
It has its own registers
that uses internally.
11:52
You don't write code for this directly.
11:55
What you really do is you write code for
the CPU.
11:58
And then the CPU kinda
pushes data to the PPU and
12:02
it handles things internally on it's own.
12:05
But we do need to kind of
set all these things up.
12:08
And we need to handle all of those
memory-mapped I/O addresses that
12:12
we looked at.
12:14
So when the CPU wants to
write to address 2007,
12:15
we need some kind of function that
handles sending data to the PPU.
12:19
So just like we wrote a function for every
opcode, we're gonna need a function for
12:24
every memory mapped I/O address
that we can call as needed.
12:28
And then we need to actually output
our graphics data in some way.
12:35
The way most emulators will do it
is they'll keep a big frame buffer,
12:38
basically just an array
of pixel color values.
12:43
The NES has a screen resolution
of 256 by 240 pixels,
12:47
so you need all of those things.
12:52
Once you have this big array set up,
just 60 times a second.
12:55
We set the color of every pixel and
then either we display it directly or
12:58
we call some kind of callback
function where you pass it.
13:03
Here's the graphics data for the next
frame, do with it whatever you want.
13:07
That could be rendering it to
a canvas element in a webpage or
13:10
drawing it to the screen in some way.
13:13
Then we can look at getting
input from the player.
13:18
We need some kind of internal
representation of what's
13:22
going on with the controller.
13:25
So we can have basically for
every button just a simple true or
13:27
false is it being pressed right now or
not?
13:31
And then we need some way of mapping those
things to something on the host computer.
13:35
Whoever is playing on your
emulator is most likely not using
13:40
a real NES controller.
13:44
So you need some way to map
whether it's keyboard keys or
13:45
touches on a touchscreen, or
whatever else they're gonna use.
13:49
Into things that your emulator can
translate to pretend that it's a real NES
13:52
controller.
13:57
In this case, maybe I have like a key
event listener in my JavaScript,
13:58
where I can just switch
based on event.code.
14:03
And if I see like a Z on the keyboard,
14:06
then I can say the A button
is currently pressed.
14:08
Now that we've got kinda the basics down,
the next thing to look
14:15
at is gonna be timekeeping, and
we need really accurate timekeeping.
14:19
So the 6502 inside of the NES,
it runs at 1.79 megahertz.
14:24
And every instruction that we looked at,
14:31
takes somewhere between two to
seven CPU cycles to execute.
14:33
And we need to know for every instruction,
just how long it has actually taken.
14:38
This is what we call a cycle accurate
emulation, where we know down to
14:43
the individual processor cycle
where we are in running the code.
14:47
We just basically need to update every
opcode function that we've already
14:51
written, and add in some ability to
track how many cycles have gone by.
14:56
So in this case, that a9,
LDA immediate mode,
15:01
that's a thing that takes two cycles for
the processor to run.
15:03
So we're gonna have some kind
of global cycle count variable.
15:07
And every time we run this function,
we're just gonna increment that by two.
15:11
So why do we need to do all of this
precise, accurate timekeeping,
15:15
you might be wondering.
15:18
Well, we have many things going on, and
we need to keep all of them in sync.
15:20
So we've looked at the CPU
running all the CPU code, but
15:25
we also need to emulate
the graphics processor.
15:28
We need to emulate audio which I'm
not even gonna talk about cuz I don't
15:31
have time.
15:35
A lot of cartridges can have what
are called mapper chips inside of them.
15:36
They're kind of like very small,
15:39
very special purpose processors within the
cartridge that we need to emulate as well.
15:41
And the only way you can
keep them all in sync is by
15:47
knowing exactly where the CPU is in time.
15:50
A lot of emulators will use what we
call the catch up method to make
15:53
this performance.
15:57
Basically what this means is you run
your CPU code until something important
15:59
happens.
16:04
So that can be, it's time to
draw the next frame of graphics.
16:04
That can be something in your CPU code.
16:08
Is trying to use one of those memory map
I/O addresses to talk to a different part
16:11
of the system.
16:15
And as soon that happens,
you just kind of put the CPU on pause.
16:17
And then you run everything else until it
has caught up to where the CPU was at in
16:20
terms of its cycle count.
16:24
This may sound kind of slow.
16:27
It may sound inefficient,
but the NES as we saw,
16:28
it's running at about two megahertz.
16:31
And modern computers, whatever you're
gonna run your emulator on, they are much,
16:34
much, much faster.
16:38
So you can afford to make
these kinds of delays.
16:39
That's kind of the overview.
16:44
Where do you go from here if this
is something you're interested in?
16:47
First of all,
hardware documentation is a huge help.
16:50
There's no way you could get this
done without having really thorough
16:53
documents on how the NES hardware works.
16:58
The best source of that I think is
the NESDev Wiki at wiki.nesdev.com.
17:00
They also have a forum that you can use,
lots of people participate there and
17:05
answer questions.
17:09
You can go to 6502.org for
more generic 6502 processor information.
17:11
There's also a website called
visual6502.org which has a full down
17:17
to the transistor level simulation
of how this processor works.
17:21
You can actually give it assembly code and
watch it run in real time.
17:25
You can learn from other
open-source NES emulator projects.
17:31
Like I said,
there are thousands of these and
17:34
they're available in pretty
much every language.
17:36
JSNES is a particularly great JavaScript
emulator that you can run in the browser.
17:40
Maison is a great C++ emulator.
17:45
There's Nintaco in Java.
17:47
And then as I'm showing here,
there's emulators in Rust, in Go, Haskell.
17:49
You name it,
it probably has an NES emulator.
17:53
You can read a book.
17:58
Nathan Altice has this great book
from MIT press called I AM ERROR.
18:00
It is a somewhat high level overview
of the deep hardware of the NES, and
18:04
the design decisions that
went into that hardware.
18:09
This is the book that got me interested
in doing NES programming specifically in
18:12
the first place.
18:16
I highly recommend it.
18:17
You can also potentially
try writing your own game.
18:20
Writing an emulator is actually harder
than writing a game in NES, in my opinion.
18:23
I've been working on an open source book
to help people get started with actually
18:28
writing games in assembly for the NE.
18:32
It's available at book.famicom.party.
18:34
I also have contact information for
myself available there.
18:37
So that's all.
18:44
I think I have some time available for
questions,
18:45
so happy to answer whatever you've got.
18:47
So first question I've got here is,
what is the minimal and ideal programing
18:50
knowledge you would need in order to
undertake writing an NES emulator?
18:54
That's a really good question.
18:59
I think if you are comfortable
in any one programing language,
19:00
if you're willing to do research
I feel like it's attainable.
19:04
Definitely like writing a really full
fledged emulator can be complicated.
19:10
There's a lot of edge cases to deal with,
certain games behave in really weird ways.
19:14
But having something that
generally works and can play,
19:20
especially some of the oldest NES games
is a really good project to work on.
19:23
Got another one here, is that the code for
19:30
character progression on
a screen with a temporary object?
19:33
I'm not sure which code exactly
is being referred to here.
19:37
One of the things I showed on
the slide with setting up memory for
19:41
the PPU is what's called OAM.
19:45
It's object access memory.
19:47
That's where the NES stores
data about all of the,
19:48
basically all the moving objects
that it's drawing to the screen.
19:52
I think that might be
what you're referring to.
19:56
Another question, what is PPU?
19:59
That is the picture processing unit.
20:01
That's the acronym that for
whatever reason,
20:03
NES developers started using for
this part of the code.
20:06
I'm sorry, for
this piece of hardware inside of the NES.
20:10
Please feel free,
check out the book that I posted before.
20:14
And let me know in my email or
any other way if you have more, thanks.
20:17
>> Thank you so much, Kevin,
really appreciate it.
20:21
You need to sign up for Treehouse in order to download course files.
Sign upYou need to sign up for Treehouse in order to set up Workspace
Sign up