Creating a NES emulator in C++11 (PART 1/2)

Creating a NES emulator in C++11 (PART 1/2)

Konnichiwa! I am Bisqwit. In this video I create a
NES emulator from scratch. The process of creating an emulator
is highly technical, and I cannot possibly
go over every detail, but I will try to explain some
of the more interesting parts. The CPU in NES has 2 kilobytes of RAM, three general-purpose registers,
a 8-bit stack pointer, and a 16-bit program counter that points
to the current instruction to be executed. The purpose of an union is to provide
different views to the same data. Nearly all registers in the NES contain
bit-fields that are smaller than a byte. This RegBit object provides a portable
way to access those bitfields. I learned it from byuu. My emulator architecture is synchronous. It means that every component
runs off the same clock.processes the screen rendering in real For instance, every time the CPU executes
one clock cycle, the PPU executes three. Each component only does the things
they should do on those clock cycles. This approach has a number of benefits. For instance, it makes it easy
to provide TAS functionality. It also makes the overall emulator
design a lot simpler than otherwise. Emulation accuracy also
comes as a side effect. The downside is that the emulator
requires a beefier computer to run. Now we come to the interesting part. The instruction execution core. This function, Op, decodes and
executes the next instruction. The process is delegated to 259 Ins functions,
made from a single template. This Ins template, created
about 5 seconds from now, contains a list of 56 micro-operations. On the 6502, or the RP2A03 which NES has, every single instruction can be synthesized
from a combination of these 56 micro-ops. This list of strings is a compile-time table – that maps the opcode numbers
into lists of micro-operations. I have another video that
explains the technique in detail. In any case, with this table and the list of micro-operations we can emulate the entire CPU. The actual CPU works by a similar principle. Inside the CPU, there is a
tiny ROM table, called PLA. It means “programmable logic array”. The CPU uses this table to enable
and disable parts of the processor – that should be active on each clock cycle
depending on the instruction being executed. The micro-ops in the CPU are not
the same as listed in this program. There cannot be a direct 1—1 relationship. This list was created for this
emulator specifically. It was my way of making the source
code as short as possible, in order to fit the video
within YouTube length limits. Just like the number 12 can be created
from 4 × 3, or 2 × 6, or 16 × 3 ÷ 4, there are multiple ways to achieve
the same outcome in emulators. And in any computer programs, really. Sorry about using so tiny font in this screen. I really wanted to fit the whole instruction
decoder & processor in one screen. This code requires a rather
modern C++ compiler, by the way. All of that “if” and decoding magic
happens completely at compile-time. The compiler physically creates 259
different functions from the same template. Next up, reading the NES ROM file.
Parsing the iNES file header. I am using C functions here, again
for source code length reasons. The iNES format is a de-facto standard
for NES ROM distribution. It was devised by Marat Fayzullin. The format has a number of problems,
but it is compact and easy to parse. It does not accurately convey the
differences between different gamepaks. A NES emulator not only needs
to emulate the console hardware, but the cartridge (GamePak) hardware as well. Gamepaks of different games have
different amounts of ROM and RAM on board. Not only that, but they also
have different circuitry. The circuitry is responsible for
things like memory “mapping”. The address space in 6502 is very small. It can only see a small amount of memory
at a time, called a “page” or “bank”. The “mapper” changes which part of the
ROM or RAM is seen through that page. Now, the PPU: Picture Processing Unit. The PPU is the graphics chip in the NES. Like the CPU, it also has a number of registers. The PPU has very little memory,
just a couple of kilobytes. But what’s important is how it uses it. NES cannot render single pixels at
arbitrary positions on the screen. (There are ways around that, but bear with me, I’m trying to explain things easy and understandable way.) Instead, the screen is divided
into 8×8 pixel cells, or “tiles”. You can only change an entire tile. The tile can be chosen from 256 pre-determined tile patterns supplied in the game ROM. The patterns are 2-bit: Each pixel
is one of four possible colors. Each group of four tiles, i.e. 16×16 pixels,
shares the list four possible colors. Different 16×16 squares on the screen
can have different sets of four possible colors, but there can only be a total of four different sets
of four possible colors on the entire screen. And the background color (one of the four colors)
must be the same in all four sets. This means there can only be 1 + 4×3=13 different colors in the entire screen of background graphics. These sets of colors are called “palettes”. Each color in the palettes comes from
a single global palette of 54 colors. Do an image search for “NES palette”
if you want to see the 54 colors. The list comes directly from
the NTSC/PAL encoding scheme. There are also a few status bits (color de-emphasis)
that can globally tweak the palette. The background graphics is freely
scrollable in all four directions. On top of the background,
there can be sprites. Sprites are also 8×8 pixel tiles. Or sometimes, 8×16. The sprites are very similar to background tiles,
but they can be positioned anywhere on the screen. Game objects such as Mario or powerups
are nearly always sprites. There can be a total of 64 sprites
on a screen simultaneously. But only eight may be rendered
on a single scanline simultaneously. If there are more, the general rule of thumb
is that the extra sprites will not show. Most games work around this limitation
by rotating the sprite list. This way, if there are sprites that
become invisible due to the limits, at least it’s a different sprite each frame. This will appear as blinking while playing. The PPU renders the screen in real time, in sync with the television set receiving
the NTSC video signal from the PPU. It runs on a very tight schedule. On each and every clock cycle
the PPU does some very specific things. Usually more than one thing at once. The design is quite clever, really. This is because there are electrical limitations. To access the cartridge memory – the PPU must first assert the memory
address, and then wait for the data. On every clock cycle it is either asserting a particular memory address, or reading the data and acting on it. But while it is waiting for
the cartridge to respond, it is also inspecting the sprite list – and sorting it out for the next scanline. And while doing that, it is also reading
the list that it created the last scanline – and rendering sprites based on that information. It all happens on a very specific schedule – where not a single clock cycle is wasted. Some games actually depend on this schedule. An accurate emulator must replicate this scheme. It was only reverse engineered quite recently. Even after I made this video, new
discoveries were still being made – by hobbyists reverse engineering the NES. This kind of information is not readily available. Console manufacturers don’t
just publish these specifics. All emulators stand on countless
of manpower hours of research. So does mine, too. I have only done
a very very tiny fraction of the research – that my emulator is based on. For the NES, you can find nearly
all of this information – at . For more modern consoles,
there may not even be such sites. If you want to create an emulator
for the newest hot game console, you must be prepared to invest
months or maybe years of time – researching the hardware and
software in that console. It is a specialty that requires very deep expertise. Maybe you can estimate how much that expertise,
multiplied by years of work, is worth on the job market. It is not something that
people do “just like that”. Think about that, if you plan to ask someone to create an emulator for your favorite console. The PPU is by far the most complex
component in the NES. Even then, it is so much simpler than the PPU of
more modern consoles like the SNES or the GBA. Games for the NES of course
have to communicate with this PPU. Game developers must know which exact sequence
of writes to do to accomplish a particular outcome. This of course makes NES games
tricky to translate to other platforms. The games assume the presence of
very specific hardware, and they are designed around the limitations
and features of the NES hardware. It is usually therefore impossible to just translate
a NES game into a more modern platform. Any such pursuit would need to incorporate
parts of an actual NES emulator. The NES was designed to
render NTSC or PAL video. Most NES emulators contain
a hardcoded palette file – that translates between
PPU color indexes and RGB colors. But the real NES does not have that. Neither does my emulator. In these few lines I created an NTSC modem. Modulator + demodulator. It encodes the PPU colors into NTSC signal samples, and straight away decodes that NTSC signal. This produces very authentic looking video. Again, the design is not most efficient, but I was aiming for a design
that is fastest to type. I did add some caching to speed it up though. While the video, excluding borders,
is 256×240 pixels in size, each “pixel” actually is eight NTSC video samples. So the resolution is 2048×240 really. However, you need 12 samples
to decode color accurately, so the horizontal color resolution is 170⅔. Within the 4:3 aspect ratio video,
is the 256×240 screen, but also horizontal borders,
for a total of 280×240. The pixel aspect ratio is therefore
4÷3×240×280=8:7. The joypads appear to the NES
as simple 8-bit shift registers. Indeed, inside the controller there is
nothing but a single TC4021 chip. The music changed. This means that we
have the next component in the emulator. I am now writing the actual memory mappers. Memory mapping is implemented using pointers. The emulator does not access memory directly. Instead, it uses a pointer to access memory. And this pointer is changed by the mapper, to point to whichever part of the memory – that the mapper circuit is
programmed to point into. It surprised me actually that C++ allows the kind
of template parameters I used in SetPages(). In this emulator I only support
five most common mappers. In reality there exist hundreds
of different mappers for NES. Every single NES/Famicom gamecart
can count as a new mapper, if it works the slightest bit differently
than any other existing mapper. Mapper 1 (MMC1) appears quite complex,
but there are far more complex mappers. I didn’t write full UI. This emulator reads joypad input from a file rather than from a keyboard. Here is the memory map
from the view point of the CPU. The soundchip is integrated into the CPU in the NES. PPU DMA and joypad input are
also mapped into that memory region. Begin section: APU (Audio Processing Unit) The APU has registers too. In the Japanese Famicom, the gamecart
may also contain audio chips, but my emulator only does the integrated audio
that is common to both NES and Famicom. All of the music that you hear in this video
is actually generated by this very emulator. The music in NES is produced using signal generators. The program / game can adjust those
signal generators in real time, varying things like the pitch (frequency) and volume, to generate impressions of different instruments. The signal generators are: Two square wave generators, one triangle wave generator, and one static noise generator. There is also a wave generator that
can read DPCM sample data from ROM. In this part I am only creating
the first four channels though. Again, the design principles are: Synchronous, i.e. it only runs cycles in
sync with the CPU instruction core, and minimal source code length,
to fit things in a YouTube video. And of course, accuracy.
I want it to sound good. The best way to make it sound good – is to do exactly what a real NES does. This would not have been possible
without the very detailed research – published for free by people like
Martin Korth, Shay Green, and others. The signal generators that produce sound – are run directly from the CPU clock. The game divides that clock to
produce sounds of different pitch. This means that if you change the CPU
clock, sounds will also be affected. The CPU clocks in NTSC and PAL
NES consoles are different. If you put an NTSC game into a PAL console, and the game does not check
which console it’s running on – i.e. it assumes it is running on NTSC,
but it is really running on PAL, you will get slower and lower-pitched
music than when running it on NES. This means e.g. that players of Mega Man II
got differently paced music – depending which hardware and region
they were playing on. Some games, such as Super Mario Bros. 3,
were changed for their PAL port, so that the music sounded almost
the same as in the NTSC release. But if you put the cartridge into a different
console, it would still sound wrong. This phenomenon was very familiar to PAL region players who bought pirate multigame carts, because these multigame carts nearly always
would contain NTSC versions of the games. Aside from the sound and speed differences, most games would still be totally playable
even if you put them in a different region console. However, if you tried plugging
an NTSC NES into a PAL television set, the situation would be quite different. While the principles behind
NTSC and PAL video are same, there are small details in timings
and in how colors are encoded. Now, as an after-thought, I decided to incorporate
DPCM support in my APU emulation. For the sake of completeness, you know. It will take about a minute. Of course in real life it took
a couple of days or maybe a week. After all, this video is a recreation, illustration. The purpose is to present the design
in a natural, followable format. For details, check out that other video
that shows how I make these videos. The soundtracks in this video, as I said earlier,
are rendered by this very emulator. The songs it is playing come from
a Japanese detective series: Jake Hunter. Or, Tantei Jingūji Saburō (探偵神宮寺三郎?). I chose some of my favorite songs
from those Famicom games. Apparently, some of those games have
been recently remade for the NDS – and even released in English. We are approaching the end of this video. Check out part 2, in which I run the emulator
and showcase some other pieces of code!

100 thoughts to “Creating a NES emulator in C++11 (PART 1/2)”

  1. Looks like YouTube no longer supports "video responses", thanks to the Google+ abomination.
    So I'm making this post to point attention towards the follow-up posts:
    — Part 2 (running the emulator):
    — The CPU emulation core explained:

  2. I am trying to learn C++. My ultimate goal is to make a free emulator that is equivalent to the paid ones and put it on Google Play by the end of the year. I watched your video and I'm scared lol.

  3. How do people even begin to create emulators for newer consoles? I imagine it starts with researching the hardware and all that obvious stuff, but after that? Is it just a huge game of guess and check?

  4. Hey, I'm back again. Now I know pointers, but I still suck. Even though I'm good at school stuff (easy as hell), I feel like, if you know 100%, I know negative 5%. Can you tell me what books or websites or notes to refer to if I want to be 50% of as good as you are? And I am really interested in memories I've never lived through — I love DOS. So, any books or guides that would get me to programming practical software for DOS would be really helpful.

    I don't want to be good to make money, I just love computers and I just want to have the skill to make WHATEVER my brain cooks up.

    Thanks! I love your videos!

  5. I liked the comments you wrote for the video and i do understand that you are writing compact code. But this code is seriously obscure even with comments, not that I am really any good in C++. I do not even want to think about trying to debug an issue in some of those sections with all the unknown hex values thrown around and shifted on the same line as conditionals and parenthesis, and single letter variable names. I think some good examples of what I am talking about are the single line for the NTSC modulator and demodulator, or the code that gets the Data, or some of those PPU lines. Will have to check out that nesdev you mentioned though, sounds cool.

  6. If you want YouTube to force captions on for this video you could add "yt:cc=on" as a tag.
    Just a small tip for your unnarrated videos

  7. So much talent goes into this. Most professional programmers can not synthesize something nearly this complex as it requires not only knowledge, but applied ability (which in large part, underlies creative ability). As I began working with programmers I was shocked to learn so many of them, never produced their own programs. Many others, create 1 or 2 simple programs in their career, with rather fruitless purpose.

  8. This code is a monstrosity. You make really cool stuff, but the readability is horrible even for C++… An entire NES emulator in ONE FILE?!?!?

  9. Love your videos and have lots of respect for your skill, but please don't say "C++11", when its not "11" and hardly "C++".

  10. Can you please go over this in detail? It looks to me as if it were almost like a VM. Great video by the way! Keep up the great work! 👍👌🙂👍👍👌

  11. I would like to say; what an impressive piece of art! I'm self taught in C/C++ myself and I'm geared towards 3D graphics programming, physics simulations, animations, AI etc. I've used many different APIs and Libraries over the years. I understand Assembly to a certain degree, but I'm no pro nor efficient at it. I would consider myself past intermediate to nearly advanced in C/C++ though. I understand a majority of all the mathematics involved from basic Algebra to Linear & Vector Calculus to Analytical Geometry and much more. I understand both Physics & Chemistry quite well. And for being self taught and knowing how to program fairly well, I can actually say that I am quite impressed with his works. I myself was interested in doing the same thing, trying to write an emulator in C++ for the NES just to improve not only my C/C++ skills but to develop a better or solid foundation in Assembly with the use of a good hex editor as well as Disassembly. Outside of this; this is my advice to anyone who is willing to try something to build something of this degree, if you are willing to do the research, trial and error and put your mind to it, anything is possible. I feel that what I'm about to propose next is crucial for any level developer or programmer who wants to understand the finer details of the bigger picture; take a look at the following below:

    For those who are not sure of what he's doing; learn binary! Once you master binary and the operations on them and understand the relationship between binary and hexadecimal you can easily figure out what is going on. For simplicity I'll show the first few patterns of a single byte:

    decimal hex binary bitwise operators: logical operators:
    log 10 log 16 log 2
    0 0x00 0000 0000 & bitwise AND && logical AND
    1 0x01 0000 0001 | bitwise inclusive OR || logical OR
    2 0x02 0000 0010 ^ bitwise XOR (eXclusive OR) ! logical NOT
    3 0x03 0000 0011 << left shift
    4 0x04 0000 0100 >> right shift != logical XOR or
    5 0x05 0000 0101 ~ bitwise NOT (one's complement) (unary) relational Not Equal
    6 0x06 0000 0110
    7 0x07 0000 0111 &= bitwise AND assignment
    8 0x08 0000 1000 |= bitwise inclusive OR assignment
    9 0x09 0000 1001 ^= bitwise exclusive OR assignment
    10 0x0A 0000 1010 <<= left shift assignment
    11 0x0B 0000 1011 >>= right shift assignment
    12 0x0C 0000 1100
    13 0x0D 0000 1101 bitwise operator table:
    14 0x0E 0000 1110 |——————————————————-|
    15 0x0F 0000 1111 | (p) | (q) | (p&q) | (p|q) | (p^q) |
    16 0x10 0001 0000 | 0 | 0 | 0 | 0 | 0 |
    17 0x11 0001 0001 | 0 | 1 | 0 | 1 | 1 |
    18 0x12 0001 0010 | 1 | 1 | 1 | 1 | 0 |
    … | 1 | 0 | 0 | 1 | 1 |
    255 0xFF 1111 1111 |——————————————————–|

    If you look at the hex and binary patterns from 0 to 15, these repeat! There are no other sequences than these, you will see longer strings of these in concatenation as you can see when we get to 16 in decimal the 5 bit from the right or 4th from the left is turned on, this bit is 2^4 so with this bit alone as in {0001 0000} it has the decimal equivalent of 16 and this in hexadecimal is 0x10, From here if you compare this with 0 in decimal and 0x00 in hexadecimal the 4 most right bits match. Look at 17 and 18 in decimal and look at the pattern of hex and binary, it just repeats! The reason that binary and hex works so well together is the fact that log 16 divides evenly by log 2 where if you divide log 10 by log 2 it doesn't!… Since hex is log 16, any digit in hex of its column will be n^16. Each hexadecimal digit represents a nibble or a half byte. For every 2 hex characters is a byte. So in many programming languages the smallest data type size besides working with actual bit fields is the byte. So char or unsigned char can be represented easily in hex as 0x00 – 0xFF. Which gives 256 combinations. A word in programming is 2 bytes long such as some ints on older machines and most commonly today shorts. These would be seen as 0x0000 – 0xFFFF. Then you have a double word which is equivalent to a modern 32bit data type such as int32 and float on some machines. These will have a string of 8 hex characters. And for your 64bit or 8 byte types these would be called quad words and they have 16 hex characters.

    Once you know how binary works in full on the mathematical end, then it is a matter of knowing the programming language you are working in such as Assembly to something that is very high level like HTML5 with CSS & Java or PHP. Even your older languages such as Fortran and Basic, it is no different, it's just a matter of knowing that language's syntax (rules – grammer) and knowing how their compilers, linkers & debuggers work. After knowing this, then it is a matter of know the actual hardware that you are giving instructions to. You have to know their design and how to program them. So having the knowledge of Assembly along with a very good Hex Editor is in my honest opinion Paramount to be a developer who wants to excel to the highest possible heights in their particular field. Many would try to argue that you don't need to know Assembly, but I kind of beg to differ. Here's why, when you are programming anything on a piece of an equipment if you under stand Assembly with the registers, accumulators, stack pointers etc and you have the documentation available for that particular machines instruction set. Then there is no limit on what you can do with that machine, it become easier to understand how to debug and find errors in your programs, and it helps you to avoid caveats and pitfalls that at first glance are unseen, such as round off errors in floating point arithmetic due to loss of precision. You don't necessarily need them to program as if all you do is some scripting language, but knowing it surely does help in the long run.

    The other important thing to know is the precedence of operators, you can easily look them up as well as knowing the ASCII table. Having a good hex editor and understanding assembly to a certain degree also helps. Knowing the architect of the machine that you are trying to program to or to emulate or simulate is also key into understanding what is going on in his code above.

    You can find the document about the 6502 processor as it is a well document due to its historical uses in past decades from the Apple to the Commodore 64, to the NES and many more. So you can find all the documentation that you need for the general purpose and instruction sets for that processor, then from there you can find the documentation for the NES which uses a version of it. Now the instruction sets and uses will vary from other platforms that have used the same processor but the overall structure is about the same. Then with the NES you have to also take into consideration RAM, ROM, the PPU, APU and the IO interfaces. After you understand the hardware, then its a matter of knowing how the software is stored on the various game cartridges that can now be found in the form of a single rom file. It's just a matter of opening that file, reading in the header, parsing its data then based on the information from the header will determine what needs to be done next, and many of the operations you will end up repeating. Now some of the stuff is easy to understand such as the PPU and fetching the sprites from the sprite table and that can easily be parsed by knowing what opcodes were returned, the I/O is fairly straight forward, but probably the hardest part of it all is normally the Sound Processing because of the fact that there are many different ways to compress and represent sound in either hardware and or software. Sound can be one of the most complicated parts of almost any program. Is the program using a mp3 format? Is it compressed or encrypted? Is it in 16 or 24bit mode? Is it MIDI? etc.

    If I was a professor at a University or a College I wouldn't necessarily be teaching this language or that… If you was going for a BA in computer science, the course would be 1 course from your first year all the way through your last, and it wouldn't just be the "typing of programs either" it would also involve hands on with the circuitry. It would start out by designing a 4 or 8 bit computer by hand, assembling it physically (bread board style). Then creating a set of op codes, then giving those op codes mnemonics thus creating your own assembly language, then creating a compiler or interpreter for that language. From there create a boot loader and a simple OS on top of it. Once you graduated the class, it wouldn't matter what firm you work for, what environment they have or what architects they use; you would know how to do just about anything on it simply because you have already designed one!

  12. I have a question for you, that's more general (I've watched the entire vid)
    I study electrical engineering and I'm thinking of getting an FPGA to let me 'experiment' irl.
    Do you happen to know if old consoles such as the S/NES were reversed engineered so I can replicate them? And if so, is it possible to 'add-on' the original circuitry extra features, like HDMI support/"TAS" options such as rewind/save on the fly while still keeping the game as close as possible to it's original state?
    Just for being able to play old games properly, nothing crazy

  13. The NES is just a signalling machine that manages interfaces between the game cartridge and the television.

    Understanding that you are effectively emulating a timing machine helps a lot in understanding how emulation works. The timings themselves, such as the number of cpu cycles an operand eats up, is the complex part that requires electrical engineering expertise. But these timings have been thoroughly explored and mapped out by electrical engineers over the years, and the issues that engineers have not explored have been mostly uncovered through trial and error.

  14. Sir I want to learn all these things but I don't that how I learn them please tell me sir or if you can teach then please teach me sir

  15. What language do you think is worth learning when you're starting out? Some say C, some say Python, some say straight C++ and I've seen many tell me that I should start by focusing on learning algorithmic thinking so I'm not sure how to get into coding.

  16. @bisqwit are there any materials that can teach me more about how the CPU work. I have read the nes wiki but I wonder if there's more as I have no clue for the CPU part of the code

  17. is it possible to incorporate satoshi/bitcoin into a nes emulator? I am looking to partner with someone to develop the worlds first nes bitcoin emulator/ open source for all free for everyone to enjoy..if i can find the right help?

  18. Man, my dream job is to become a game designer/programmer and honestly I don’t understand this at all! Likewise I haven’t studied it yet but this seems far out of my league.

  19. I like how the music shifts when you start writing those weird-looking strings in the "opcode decoding matrix"

    EDIT: HOLD ON A SECOND, are those strings ASCII conversions of the actual instructions in the ROM? How come they're so long? I'm really lost here flskdj

    ANOTHER EDIT: oooh I'll look at the walkthrough in the description

  20. "for(unsigned n=0; n<1; ++n) APU::tick();" wow, genius. Now show us the video where you re-write this to be readable and not all crammed into a single file.

  21. Emulators are really awesome,good way to preserve playing old games,long time after consoles are so rare,or not even existing,And thats really cool.

  22. I'm interested in learning how you were able to "reanimate" your code in your videos. Do you have a code retyper of some sort? Or do you need to retype the code?

  23. Music for programming with this video on background

  24. Is this the game you got the music from? Specifically at 6:38 and 12:44:

    I couldn't find the first song you played though.

  25. I guess you are quite a genius, but what is the purpose of these kind of videos? Without explaination we can only watch what you are doing.

  26. Hi, would you mind if I used a few seconds of footage from this video in a video I'm working on now?

  27. I could imagine you being causual about it…
    "Hey man, how's your day?"
    "Oh nothing man, I just made myself an NES emulator, nothing unusual, you?"

  28. And here I am a couple of weeks into learning C, making a box move across the screen saying yeet.

  29. The legend says that he created C and C++, and not Dennis Ritchie(C) or Bjarne Stroustrup(C++).

  30. you maybe lightning fast writing code because the timelapse at top right seems natural to me…

  31. Shouldn't Youtube Hire you already as a Programmer? You should be hired to fix their recommended algorithm.

  32. I started writing code when I was 8 year old on the Commodore64. I've been writing code for over 30 years now. I always thought I was a good coder, but you just destroyed my self-esteem!

  33. How come in your table you have 33 characters in the first argument to t() macro? Where is the last character used?

  34. I mean… im good at C++, even written an emulator myself… and it wasnt that complex or convoluted. wth did i just watch?

  35. more a bragging video than anything…
    for example this about the NES has ZERO line of code but is 1000% more interesting and insightful.

    This video has no educational value unless you already have quite a clear understanding of the SNES arch.

Leave a Reply

Your email address will not be published. Required fields are marked *