The three rules of Ruby Quiz:
1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.
2. Support Ruby Quiz by submitting ideas as often as you can:
Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion. Please reply to the original quiz message,
if you can.
by Ethan Price
CHIP-8 was an interpreted language used in the 1970's for basic games like Pong.
While it is technically an interpreted language, it defines many components a
real computer has, such as registers and a processor. Today, like many other
gaming consoles, it is kept alive by emulators allowing it to be played on most
any modern computer. Emulation is where raw operation codes (the most low-level
way of feeding instructions to a computer) are translated by another computer,
giving a totally different system the ability to run programs not designed for
Your job is to make an emulator. It will only cover some basic parts of it, but
all emulators have to start somewhere. I have written a simple program, and will
give you a list of all the opcodes I used and what they are supposed to do. We
won't worry about graphics right now, just simple operations. Four things need
to be done:
1. Make the emulator read the file. All opcodes are four digit hex numbers,
but those will need to be split.
2. Set up the registers and other assorted things so they can be modified.
3. Interpret the function of the opcode in Ruby.
4. Dump all the registers so you can check and make sure everything went well.
Like I said, all opcodes are four digit long hex numbers. So you read four hex
digits worth of data, process it, then move on to the next four digits. There
are 16 registers (areas where data can be stored). They are named V0 through VF,
and are all eight bits wide. VF is usually used as a carry for math operations.
Here is the list of opcodes you will need to interpret:
NNN is an address
KK is an 8 bit constant
X and Y are two 4 bits constants
0000 This is nothing. If you see this, it should mean that you are at the end
of the file and have nothing left to read, so you therefore need to exit
1NNN Jump to the address NNN of the file
3XKK Skip next instruction if VX == KK
6XKK VX = KK
7XKK VX = VX + KK (Note: The documentation does not mention this, but I would
assume that VF would act as a carry if needed. The
included program will not need a carry for this
function, so providing for one is purely optional.)
8XY0 VX = VY
8XY1 VX = VX OR VY
8XY2 VX = VX AND VY
8XY3 VX = VX XOR VY
8XY4 VX = VX + VY, Sets VF to 1 if there is a carry, 0 if there isn't
8XY5 VX = VX - VY, VF is set to 0 if there is a borrow, 1 if there isn't.
8X06 VX = VX SHIFT RIGHT 1 (VX=VX/2), VF is set to the value of the least
significant bit of VX before the shift.
8XY7 VX = VY - VX, VF is set to 0 if there is a borrow, 1 if there isn't.
8X0E VX = VX SHIFT LEFT 1 (VX=VX*2), VF is set to the value of the most
significant bit of VX before the shift.
CXKK VX = Random number AND KK
Lets explain how to read these opcodes. Take opcode 1NNN for example. Since the
1NNN starts with 1, we know that no matter what comes after it we will be doing
a jump instruction. Same with the other codes. For the opcode 8XY2, we know if
an opcode starts with 8 and ends in 2 it will be the operation "VX = VX AND VY".
The variables NNN, KK, X and Y are a little different. They can represent any
hex number (0-F), and also are used to note how big of chunks to read them in.
For NNN, you will want to read those as a three digit hex number. Lets do an
example opcode (in hex): 1234. Since it starts with 1, we know it is a "Jump to
address NNN" command. The three numbers after that (the NNN part), are 234, so
we know to jump to location 234 in the file (this is a hex number, not decimal).
KK and X/Y are similar, just for different sized chunks.
While I'm on the subject, let's talk about addresses in detail. Your current
address in the file is how many bytes you are from the beginning of the file. So
if I am at address 008 in the file, I am eight bytes away from the beginning of
the file, and therefore the next byte I read will be byte nine. Say we had the
opcode 100F, that means we would jump to after byte 16 (it is a hex remember).
If your wondering how the carries work, here are some examples. First off,
addition. Say we add these two numbers (opcode 8xy4):
11111111 <--This is VX
00000001 <--This is VY
Obviously this would equal 100000000, but that is too big for one register. So
what we do is make VF equal 00000001, and then roll over to 00000000. Sort of
like an old car odometer. When it hits 99,999 miles, it goes back to 00,000
miles, except we are keeping track of the fact that it rolled over. Subtraction
is similar (opcode 8xy5):
00000000 <-- This is VX
00000001 <-- This is VY
Since the registers can only deal with positive numbers, we have to keep it
positive. So what we do is "borrow", making VX equal to 100000000. All of a
sudden our subtraction works:
If we do this though, we have to make sure VF is zero to indicate that a borrow
was needed. If a borrow was not needed, VF gets set to 00000001, since you
didn't need the extra.
Now for shifts. To sum it up, if you shift left, the far left number gets taken
away, and a zero added to the far right. So 10101111 would become 01011110,
because we got rid of the far left number and added a zero to the far right.
Shift right is the same except going the other way (get rid of far right number,
add 0 to the left). As you can see, a number disappears when you shift. VF is
set to the number that disappears (obviously it can only be a 1 or 0).
A full list of opcodes can be had at David Winter's page, under section 1.5:
He also has a full explanation of everything, so if check there for more
details. If you want to write code for an opcode that I didn't list, by all
means go for it.
Be sure to start reading the attached program from the beginning. To my
understanding in a real Chip-8 game up to address 200 was reserved for machine
code which we wont worry about.
The register values you should get are:
V4:this number should be random, so do multiple runs to make sure it changes
A note: If everything is working as it is supposed to, you should run straight
through the file once. If you are looping, I would start with double checking
all your math operations.